Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- I wanted to expand a bit on Marin's GUI guide with some tricks you can use to add a little polish to your UI!
- A bit of clarification here - I'm not a graphic designer, so my focus here is more on how to /implement/ these little details, and not necessarily deciding where/if they should be added to begin with. (It's like I'm showing you how to hammer in nails and turn screws, but not how to draw blueprints, if that analogy makes any sense) I do generally think that all of these additions improve on the base design, but mostly just in the sense of "I think it looks nice", haha. (I'll clarify a bit more where I think they improve beyond just aesthetics)
- https://reliccastle.com/resources/98/
- I'm adding these in one by one and labelling them as different "versions" so you can see the progression, but I'm not doing them in any particular order. (There's no reason you couldn't make the v3 change before the v2 change, for example)
- v1 - Base version
- [code=Ruby]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- @sprites["bg"] = Sprite.new(@viewport)
- @sprites["bg"].bitmap = Bitmap.new("Graphics/UI/Boolean/bg")
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["sel"] = Sprite.new(@viewport)
- @sprites["sel"].bitmap = Bitmap.new("Graphics/UI/Boolean/cursor")
- @sprites["sel"].x = 192
- @sprites["sel"].y = 130
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- @sprites["sel"].y = 130 + (80 * @sel)
- end
- break if Input.trigger?(Input::BACK)
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- This is mainly just to let you see what we're starting with! The code is taken straight from Marin's tutorial, I've just made a few changes -
- -I've changed what images are being displayed and what coordinates they're at. (as well as some internal stuff like the class name and hash IDs) That sounds like a lot, and when you see it in-game, it definitely is - this is a totally different screen from Marin's, after all. But from a code perspective, it's not all that different - instead of telling it to display image Z at coordinates X,Y, I'm telling it to display image A at coordinate A,B. Everything's mostly following the same format!
- ((Add text-compare screenshot
- -I've commented out the code for pressing Enter for now, just because I want to save that for later in the tutorial
- -I removed Kernel., since Kernel. isn't used in Essentials anymore. (I think it was phased out in v18?)
- -Here's the more interesting one - I've added wraparound!
- Wraparound is where you can loop back to the start of a menu from the end of it.
- ((Get example in PC)
- In Marin's initial script, you can press left to move left from girl to boy, and press right to move right from boy to girl.
- ((Probably get screencap illustrating it) But you couldn't press left to move from boy to girl, or right to move from girl to boy.
- ((Get screencap)
- For GUI with only two choices like these, wraparound isn't really amazing - you're only pressing one key either way. But for GUI where there's a longer list, it's nice to be able to just loop back to the start rather than scroll back through a bunch of choices!
- ((Show summary example
- v2 - Changing value directly (introducing opacity change)
- [CODE lang="ruby" highlight="16, 32-38, 41-47"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- @sprites["bg"] = Sprite.new(@viewport)
- @sprites["bg"].bitmap = Bitmap.new("Graphics/UI/Boolean/bg")
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @sprites["sel"] = Sprite.new(@viewport)
- @sprites["sel"].bitmap = Bitmap.new("Graphics/UI/Boolean/cursor")
- @sprites["sel"].x = 192
- @sprites["sel"].y = 130
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- @sprites["sel"].y = 130 + (80 * @sel)
- if @sel == 0
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- else
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- A nice, easy addition - when the cursor moves, the button that isn't selected will become slightly transparent, while the button that is selected will become fully opaque. I personally think this is helpful to more clearly communicate to the player which option is currently selected.
- If you've used the Show Picture/Move Picture commands in events, you'll be familiar with the different properties of an image that you can edit. With event commands, this includes
- -Coordinates
- -X-Zoom and Y-Zoom
- -Opacity (how transparent/solid an image is) - 0 is fully transparent, 255 is fully opaque.
- -Blend type
- -Tone
- You can also use the Rotate Picture and Change Picture Color Tone commands to change the angle and tone, respectively.
- With scripting, you can do the same things! It's just going to be done through code rather than event commands. Sometimes, this is a little harder - it's a lot nicer setting the picture tone via event, where you can see what the tone looks like. But a lot of times it offers new possibilities, because you can add math functions that you couldn't do via events! A common one is checking Graphics.width or Graphics.height to position images based on the screen size, rather than getting the exact width/height and working from there. Graphics.width/2 is a little easier to remember than "256, or whatever half of what my customer screen size is", and it's easier to adapt to other screen sizes, too!
- There's a few other attributes of a Sprite object that aren't done with the usual Game_Picture class, stuff like color (different from tone), flash, and wave. I'm not as familiar with these concepts, but you can read the Ruby documentation about them here.
- ((add link
- https://www.rubydoc.info/gems/openrgss/Sprite
- Note that here, I'm setting the values immediately - the images aren't fading in/out, they're just immediately being set to the new value. It's just like how the cursor moves - you don't see it moving to the new position, it just immediately appears there. But what if I wanted to show it making that transition? In an event, I'd set the number of frames to move it, but as a script, I'm going to have to take a different approach.
- v3 - Changing value over time (adding moving cursor)
- [CODE lang="ruby" highlight="21, 29-33, 37"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- @sprites["bg"] = Sprite.new(@viewport)
- @sprites["bg"].bitmap = Bitmap.new("Graphics/UI/Boolean/bg")
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @sprites["sel"] = Sprite.new(@viewport)
- @sprites["sel"].bitmap = Bitmap.new("Graphics/UI/Boolean/cursor")
- @sprites["sel"].x = 192
- @sprites["sel"].y = 130
- @selgoal = 130
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- @selgoal = 130 + (80 * @sel)
- if @sel == 0
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- else
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- Be sure to cover - making sure the dif is divisible, could make pbUpdate its own method
- Here, I've changed the code so that when the cursor is moved, you see it sliding to its new position, rather than have it just appear at its new coordinates. That's another nice feature of changing these properties via script - we can directly add/subtract/multiply/etc these values, rather than setting them to specific numbers each time. So in this case, rather than tell the game "Change sel's y value to 130, then to 135, then to 140, then to 145...", I just tell it "add 5 to sel's y value until it gets to 210".
- The most important thing to remember is to run Graphics.update after the change is applied, so it'll actually be displayed. Beyond that, you've got a variety of ways you can go about this, depending on how you want your code arranged. I'm not going to cover every possible method, but I'll briefly show an alternative way this could be done, and explain my reasoning behind why I've arranged my code like this.
- Here's a version of the code where this is done via its own loop and only uses constants, rather than using variables like dif and @selgoal.
- [CODE lang="ruby" highlight="34-37, 41-44"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- @sprites["bg"] = Sprite.new(@viewport)
- @sprites["bg"].bitmap = Bitmap.new("Graphics/UI/Boolean/bg - Copy")
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @sprites["sel"] = Sprite.new(@viewport)
- @sprites["sel"].bitmap = Bitmap.new("Graphics/UI/Boolean/cursor")
- @sprites["sel"].x = 192
- @sprites["sel"].y = 130
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- if @sel == 0
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- 16.times do
- @sprites["sel"].y -= 5
- Graphics.update
- end
- else
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- 16.times do
- @sprites["sel"].y += 5
- Graphics.update
- end
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- ((v3a video)
- You can see from the video that this doesn't really seem very different in-game. The only difference from a player's perspective is that the animation played out in full before control was returned to the player, while in my original version, I could switch back while the cursor was still moving. It's a pretty subtle difference, especially with an animation this fast, but it is something worth considering depending on what you're using your UI for.
- Everything else only matters from the dev's perspective, but I find these changes to be pretty handy all the same!
- -There's less redundant code. This doesn't matter a whole lot (especially since, with the additions in the goal version, this one is only one line longer), but I personally like to reduce redundancies as much as possible.
- -In the loop version, my end result isn't written anywhere, I have to do math to figure out the final value. (starting value + (times looped * value added)) In the goal version, I've set the end value directly, and use that as the base for my math instead.
- --For example, say I want to space out the buttons a little more, and I want the cursor's y-value at 230 instead of 210. In my goal script, I'd just change this line -
- @selgoal = 130 + (80 * @sel)
- to
- @selgoal = 130 + (100 * @sel)
- But in my loop script, I'd have to do a quick bit of math - 130 + 5x = 230 - and then change the number of loops both times it's called.
- 20.times do
- @sprites["sel"].y -= 5
- Graphics.update
- end
- 20.times do
- @sprites["sel"].y += 5
- Graphics.update
- end
- It's not hard work in the slightest, but it's just a little more tedious IMO, especially when you're playtesting different values to figure out how you want your UI.
- Similarly - since I don't have a set number of loops, I can change the speed more easily. If I want to try doubling the speed - moving 10 pixels per frame instead of 5 - then on the loop version, I have to change both the number of loops and the number of pixels, both times they're called.
- 8.times do
- @sprites["sel"].y += 10
- Graphics.update
- end
- 8.times do
- @sprites["sel"].y -= 10
- Graphics.update
- end
- But on the goal version, I only have to change one line.
- dif = 10
- Again, the extra steps in the loop version aren't hard by any means, but if you can cut them out and save time, why not?
- Anyways! Now that I've made my pitch, let me outline the actual process behind my goal method:
- -In initialize, set an instance variable @goal with the default value for the attribute in question
- -In the main loop:
- --Create an variable dif that is an integer with a common factor between the different goal values. In other words, whatever goal values you have should all be divisible by dif.
- --Check if the attribute is greater than dif. If it is, multiply dif by -1. (This makes sure the value is decreasing if the goal is below it, and increasing if the goal is above it.)
- --Add dif to the attribute if the attribute is not equal to the goal value
- -
- So, in my code, I'm applying this method with the y-value of the sel sprite, @sprites["sel"].y. It starts at 130, and moves between 130 and 230.
- -In initialize, I set the instance variable @selgoal at 130.
- -In the main loop:
- --I set the variable dif to 5, because both 130 and 230 can be divided by 5.
- --I check if @sprites["sel"].y is greater than @selgoal, and if it is, multiple dif by -1.
- --I check if @sprites["sel"].y is equal to @selgoal. If it's not, I add dif to @sprites["sel"].y.
- --When the player selects "No", I change @selgoal to 230, and when they select "Yes", I change @selgoal back to 130.
- It's important that dif is a common factor of your goal values, because if it's not, then your attribute will never land on your goal value, and the check to see if it's reached will always fail. For example, if I set dif to 60 here, it would go 130, 190, 250, and completely skip over 230. Now it's over 230, so it'll try going down - but it'll go down by 60, so it's back to 190, and it's skipped 230 again.
- There are ways around this - you could make your check if the attribute is equal to or less than/greater than the goal, but now you've got to check which direction you intended to go in, and for that much work, you might as well just adjust your goal values or switch to the loop method. You could also have a check for if dif is greater than the actual difference between the two values, and if it is, just set it to the actual difference. (Something like dif = (@goal - value).abs if dif > (@goal - value).abs ) That means it would be moving a bit faster at the end, but that might not be noticeable.
- Hopefully this all makes sense - I'm trying to strike the right balance between clearly labelling things and not bogging things down with math/scripting terms, haha.
- v4 - Looping Background Planes
- We've played with the buttons and the cursor, now how about the background?
- Gen 5 included a lot of neat looping BGs
- [CODE lang="ruby" highlight="6, 27"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- addBackgroundPlane(@sprites, "bg", "Boolean/bg", @viewport)
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @sprites["sel"] = Sprite.new(@viewport)
- @sprites["sel"].bitmap = Bitmap.new("Graphics/UI/Boolean/cursor")
- @sprites["sel"].x = 192
- @sprites["sel"].y = 130
- @selgoal = 130
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- @sprites["bg"].ox-= -1
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- @selgoal = 130 + (80 * @sel)
- if @sel == 0
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- else
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- Be sure to explain formatting, how the movement works - x-1 is the looping in the video, how to slow down
- COuld do GIF but programming it is easier to tweak
- Mention the arguments pbAddBackgroundPLane takes
- v5 - change cursor to animated arrow
- include -
- -Arguments taken for animated sprite
- -How I got the new coordinates
- -The .update in the loop
- [CODE lang="ruby" highlight="16-20, 29, 38"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- addBackgroundPlane(@sprites, "bg", "Boolean/bg", @viewport)
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @sprites["sel"] = AnimatedSprite.new("Graphics/UI/right_arrow", 8, 40, 28, 2, @viewport)
- @sprites["sel"].x = 156
- @sprites["sel"].y = 150
- @sprites["sel"].play
- @selgoal = 150
- main
- end
- def main
- loop do
- Graphics.update
- Input.update
- @sprites["bg"].ox-= -1
- @sprites["sel"].update
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- pbPlayCursorSE
- @sel = (@sel+1)%2
- @selgoal = 150 + (80 * @sel)
- if @sel == 0
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- else
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- v6 - adding mouse function
- Mention -
- -arguments that rectangles take
- -Might want to check bigger area
- -Remember to allow for possibility mouse isn't there
- -Remember to consider when mouse is over button that's already selected
- [CODE lang="ruby" highlight="11, 17, 26-40, 46, 55-65"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- addBackgroundPlane(@sprites, "bg", "Boolean/bg", @viewport)
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @yesrect = Rect.new(@sprites["yes"].x,@sprites["yes"].y,@sprites["yes"].width,@sprites["yes"].height)
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @norect = Rect.new(@sprites["no"].x,@sprites["no"].y,@sprites["no"].width,@sprites["no"].height)
- @sprites["sel"] = AnimatedSprite.new("Graphics/UI/right_arrow", 8, 40, 28, 2, @viewport)
- @sprites["sel"].x = 156
- @sprites["sel"].y = 150
- @sprites["sel"].play
- @selgoal = 150
- main
- end
- def sel_yes
- pbPlayCursorSE
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- @sel = 0
- @selgoal = 150
- end
- def sel_no
- pbPlayCursorSE
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- @sel = 1
- @selgoal = 230
- end
- def main
- loop do
- Graphics.update
- Input.update
- mousepos = Mouse.getMousePos
- @sprites["bg"].ox-= -1
- @sprites["sel"].update
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- if @sel == 0
- sel_no
- else
- sel_yes
- end
- end
- if mousepos
- if @yesrect.contains?(mousepos[0], mousepos[1]) && @sel == 1
- sel_yes
- elsif @norect.contains?(mousepos[0], mousepos[1]) && @sel == 0
- sel_no
- end
- end
- break if Input.trigger?(Input::BACK)
- #if Input.trigger?(Input::C)
- # cmd = pbConfirmMessage(_INTL("Are you sure you're a #{@sel == 0 ? "boy" : "girl"}?"))
- # if cmd
- # pbChangePlayer(@sel)
- # break
- # end
- #end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- v7 - Adding animation by changing bitmap
- We're finally uncommenting that code!
- [CODE lang="ruby" highlight="69-92"]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- addBackgroundPlane(@sprites, "bg", "Boolean/bg", @viewport)
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/yes")
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @yesrect = Rect.new(@sprites["yes"].x,@sprites["yes"].y,@sprites["yes"].width,@sprites["yes"].height)
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/no")
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @norect = Rect.new(@sprites["no"].x,@sprites["no"].y,@sprites["no"].width,@sprites["no"].height)
- @sprites["sel"] = AnimatedSprite.new("Graphics/UI/right_arrow", 8, 40, 28, 2, @viewport)
- @sprites["sel"].x = 156
- @sprites["sel"].y = 150
- @sprites["sel"].play
- @selgoal = 150
- main
- end
- def sel_yes
- pbPlayCursorSE
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- @sel = 0
- @selgoal = 150
- end
- def sel_no
- pbPlayCursorSE
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- @sel = 1
- @selgoal = 230
- end
- def main
- loop do
- Graphics.update
- Input.update
- mousepos = Mouse.getMousePos
- @sprites["bg"].ox-= -1
- @sprites["sel"].update
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- if @sel == 0
- sel_no
- else
- sel_yes
- end
- end
- if mousepos
- if @yesrect.contains?(mousepos[0], mousepos[1]) && @sel == 1
- sel_yes
- elsif @norect.contains?(mousepos[0], mousepos[1]) && @sel == 0
- sel_no
- end
- end
- break if Input.trigger?(Input::BACK)
- if Input.trigger?(Input::C) || Input.trigger?(Input::MOUSELEFT)
- Input.update
- pbPlayDecisionSE
- button = (@sel == 0) ? "yes" : "no"
- @sprites["sel"].visible = false
- Graphics.update
- @sprites["#{button}"].bitmap = Bitmap.new("Graphics/UI/Boolean/#{button}1")
- 12.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].bitmap = Bitmap.new("Graphics/UI/Boolean/#{button}2")
- 8.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].bitmap = Bitmap.new("Graphics/UI/Boolean/#{button}3")
- 30.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- break
- end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- Here, I've got it just directly changing the bitmap of the sprite by loading a new one. This is okay on its own - you can see in the video that it runs just fine - but it does mean I have four image files for each button, and the game is loading a new bitmap three times in the animation. Let's see if we can simplify that a bit by making these buttons one sprite sheet that the game moves through!
- We're going to be making use of the src_rect property of a sprite! A sprite's src_rect (short for "source rectangle") is the portion it takes from its base bitmap. This is usually the whole image, of course, but when we want to select just part of it, we'll start manipulating the src_rect command! (This is actually how the standard spritesheets in the overworld work!)
- sprite.src_rect.set(x,y,width,height) will create a new src_rect for the sprite in question, starting at the point (x,y), and selecting the width left to right and the height top to bottom. (Sprites naturally have src_rects by default, starting at 0,0 and using the full width and height of the base bitmap, but you usually won't be manipulating a src_rect like that)
- Since this is original code, I can choose to format my spritesheet however I want it! I'm going to keep it simple and just set each frame directly below each other, like this.
- I'm still starting at 0,0, and I'm using the full width of my image, so I could actually just edit the height of the src_rect directly...
- @sprites["yes"].src_rect.height = 60
- But just for clarity's sake, I'll use the src_rect.set command.
- @sprites["yes"].src_rect.set(0,0,96,60)
- Now, instead of generating a new bitmap, I'll just change the y-coordinate of the src_rect! This coordinate is where the src_rect is on the graphic, not where the image is displayed on my viewport, so it's just making it select from a different portion!
- [CODE lang="ruby" highlight="4, 9, 14"]
- button = (@sel == 0) ? "yes" : "no"
- @sprites["sel"].visible = false
- Graphics.update
- @sprites["#{button}"].src_rect.y += 60
- 12.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].src_rect.y += 60
- 8.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].src_rect.y += 60
- 60.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- [/code]
- And there we go! It works just as well as the previous code, and now my sprites are a bit more organized, too!
- ((insert - v7a))
- [code=RUBY]
- class BooleanUI
- def initialize
- @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
- @sprites = {}
- @sel = 0
- addBackgroundPlane(@sprites, "bg", "Boolean/bg", @viewport)
- @sprites["yes"] = Sprite.new(@viewport)
- @sprites["yes"].bitmap = Bitmap.new("Graphics/UI/Boolean/button_yes")
- @sprites["yes"].src_rect.set(0,0,96,60)
- @sprites["yes"].x = 192
- @sprites["yes"].y = 130
- @yesrect = Rect.new(@sprites["yes"].x,@sprites["yes"].y,@sprites["yes"].width,@sprites["yes"].height)
- @sprites["no"] = Sprite.new(@viewport)
- @sprites["no"].bitmap = Bitmap.new("Graphics/UI/Boolean/button_no")
- @sprites["no"].src_rect.set(0,0,96,60)
- @sprites["no"].x = 192
- @sprites["no"].y = 210
- @sprites["no"].opacity = 150
- @norect = Rect.new(@sprites["no"].x,@sprites["no"].y,@sprites["no"].width,@sprites["no"].height)
- @sprites["sel"] = AnimatedSprite.new("Graphics/UI/right_arrow", 8, 40, 28, 2, @viewport)
- @sprites["sel"].x = 156
- @sprites["sel"].y = 150
- @sprites["sel"].play
- @selgoal = 150
- main
- end
- def sel_yes
- pbPlayCursorSE
- @sprites["yes"].opacity = 255
- @sprites["no"].opacity = 150
- @sel = 0
- @selgoal = 150
- end
- def sel_no
- pbPlayCursorSE
- @sprites["yes"].opacity = 150
- @sprites["no"].opacity = 255
- @sel = 1
- @selgoal = 230
- end
- def main
- loop do
- Graphics.update
- Input.update
- mousepos = Mouse.getMousePos
- @sprites["bg"].ox-= -1
- @sprites["sel"].update
- if @sprites["sel"].y != @selgoal
- dif = 5
- dif *= -1 if @sprites["sel"].y > @selgoal
- @sprites["sel"].y += dif
- end
- if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
- if @sel == 0
- sel_no
- else
- sel_yes
- end
- end
- if mousepos
- if @yesrect.contains?(mousepos[0], mousepos[1]) && @sel == 1
- sel_yes
- elsif @norect.contains?(mousepos[0], mousepos[1]) && @sel == 0
- sel_no
- end
- end
- break if Input.trigger?(Input::BACK)
- if Input.trigger?(Input::C) || Input.trigger?(Input::MOUSELEFT)
- Input.update
- pbPlayDecisionSE
- button = (@sel == 0) ? "yes" : "no"
- @sprites["sel"].visible = false
- Graphics.update
- @sprites["#{button}"].src_rect.y += 60
- 12.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].src_rect.y += 60
- 8.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- @sprites["#{button}"].src_rect.y += 60
- 60.times do
- Graphics.update
- @sprites["bg"].ox-= -1
- end
- break
- end
- end
- dispose
- end
- def dispose
- pbDisposeSpriteHash(@sprites)
- @viewport.dispose
- end
- end
- [/code]
- Conclusion
- So we've gone from this...
- ((insert v1
- to this!
- ((insert v7a
- It was fair amount of code - it went from 41 lines to 102 lines - but nothing too complicated!
- I hope seeing these two side-by-side helps drive the point a little more - these are all pretty simple changes, but they really make things look a lot nicer!
- This script (in all its forms) is free to use as long as you credit me, TechSkylander1518. The graphics can be downloaded here. ((insert link) (I have to warn you - this doesn't actually return true or false in its current state! IMO the simplest thing to do would be to make it set a switch, and use that for whatever conditional branch you want)
- As one last bit of showing off... I thought it'd be fun to adapt Aki's [URL='http://Ribbons Style summary']Ribbons Style summary[/URL] to animate the ribbons! (and figured I'd add wraparound while I'm at it)
- ((insert akidemo
- The edited assets for this can be found ((insert link here.
- I'm not making this plug-and-play, because it's a lot simpler to make the edits than it is to rewrite several methods in the summary UI. The changes are as follows:
- [spoiler]
- All of these changes are in UI_Summary.
- 1 -
- Change this bit of code
- def pbUpdate
- pbUpdateSpriteHash(@sprites)
- end
- to
- def pbUpdate
- pbUpdateSpriteHash(@sprites)
- for i in 1..5
- if i != @selrib
- @sprites["ribbon#{i}"].y -= 2 if @sprites["ribbon#{i}"].y > -44
- else
- @sprites["ribbon#{i}"].y += 2 if @sprites["ribbon#{i}"].y < 0
- end
- end
- end
- 2 - Under this line
- @sprites["markingsel"].visible = false
- Add
- @selrib = 1
- for i in 1..5
- @sprites["ribbon#{i}"] = Sprite.new(@viewport)
- @sprites["ribbon#{i}"].bitmap = Bitmap.new("Graphics/UI/Summary/ribbon#{i}")
- @sprites["ribbon#{i}"].x = 230 + (56 * (i-1))
- @sprites["ribbon#{i}"].y = (i == @selrib) ? 0 : -44
- end
- 3 - With each method
- def pbDrawPageOne
- @selrib = 1
- def pbDrawPageTwo
- @selrib = 2
- And so on.
- 4 - Wraparound -
- Both instances of these lines
- @page = 1 if @page < 1
- @page = 5 if @page > 5
- Change them to -
- @page = 5 if @page < 1
- @page = 1 if @page > 5
- [/spoiler]
- Please remember to credit both Akizakura16 and me if you use this!
Add Comment
Please, Sign In to add comment