You are not logged in.

Applications: [GameMaster: OPEN] | [Volunteer Testers: OPEN]


This forum will be permanently shut down on Friday 13.07.2018
Please copy or save all important information from old forum before they will be deactivated
We have moved to new board. https://forum.runesofmagic.gameforge.com/Come join us.

Peryl

Intermediate

  • "Peryl" started this thread

Posts: 313

Location: Elsewhere

  • Send private message

1

Friday, March 23rd 2012, 1:12am

[Tutorial] Auto-Stretched Textures and Widgets

I've been meaning to put up a little tutorial on using auto-stretch textures (at least, that's what I call them). Guess it was about time I made one.


This tutorial will introduce you to what auto-stretch textures are and show how they can be implemented. I'll be assuming you already know how to create a simple add-on and so I'll only be presenting example code to show the concept but should be complete enough to run as a test add-on. The code assumes that the "add-on" is called AutoStretch, but this was done for finding and loading the textures themselves. Modify the paths as needed.


The code snippets I'll give here will be using dynamic frames, so if you are not yet comfortable with them or simply don't know what they are, I suggest going over my frame guides on the RoMWiki. Either way, the concepts presented here are rather straight forward and should not present too much trouble for anyone, though I will assume that you already know how to put a texture and frame on screen as well as how anchors are defined/used.


The technique described here can also be applied to more than just textures, so where appropriate, you can replace the word texture with the word widget and get the same results for that widget.




What's an Auto-Stretch Texture?
An auto-stretch texture is a term I'm using to describe a normal RoM texture that is anchored in such a way that it will automatically stretch to fill the gap it covers. This includes if the anchor points are repositioned during display of the texture. The game will effectively re-scale the texture as it draws it to the new size.


This allows for all kinds of things, including UI elements that can auto-resize themselves depending on the type of screen the user has (as in a normal or widescreen monitor). Many other uses can be imagined.




The Concept Behind the Technique
To implement auto-stretch textures, we use an ability of the game engine. Namely, multiple anchors.


When defining textures, frames, or other widgets, we define an anchor to position the texture/frame/widget. Well the game allows the declaration of multiple anchors for the game widgets and this in turn allows the ability to have auto-stretch textures, frames or whatever widget.


When using an XML file, we can define a list of anchors within the Anchors tag, while when using dynamic frames, each call to SetAnchor adds to the anchor list. This is the reason why ClearAllAnchors is needed before setting the first anchor, we need to clear out any possible anchor list the widget may already have.


Recall that when defining an anchor, we give a relative point on our texture/widget and another on the object we are anchoring to. With carefull use and planning of how we set these points, the game will automatically scale and stretch our textures for us. Because of this, we do not need to give a size to the texture since it will get its size from the positions of the anchor points, though there are times where defining a size can still be convenient.


How we position the anchor points will determine how our texture gets stretched and so the rest of this document will show how this can be achieved.




Creating a Simple Window
To show an example of how these auto-stretch textures can be used, we'll create a simple window pane with a total of nine small textures. Four of these will be for the corners of the window and will be treated as normal un-stretched textures. Four more textures will be used for the sides which will be stretched either vertically or horizontally, and the last texture will be for the window's background and will be stretched in both the vertical and horizontal directions.


I've prepared some 32x32 textures to be used that will show how these textures are being stretched. I placed little colored indicators in the corners of each texture so that you can visually see the area the texture is using. Further, some have some text embedded into them to further show how the texture is stretched. You can make your own textures, but I'll be using these example textures when deciding the anchor point offsets.

(See below for attachment with the textures)

So our first step in defining a window will be to create a frame on which these textures can be applied. Lets make our window a size of 400x300. This is obviously much bigger than the width or height of all nine textures put together so some stretching must be achieved in order to cover the entire window.

Source code

1
2
3
4
local WindowFrame = CreateUIComponent("Frame", "WindowFrame", "UIParent")
WindowFrame:SetSize(400,300)
WindowFrame:ClearAllAnchors()
WindowFrame:SetAnchor("TOPLEFT", "TOPLEFT", "UIParent", 100, 100)





The Title Bar
Now to start placing our textures. I'll be doing this piecemeal so that each step can be tested out. Lets start with the window's title bar.


To get the title bar to stretch across the top of our window frame, we'll need some positions to anchor our texture to. Since we want the corners of the window to appear correctly (i.e. not stretched), we use corner pieces (textures) and only stretch the top texture to fill the gap in between the two. This will become a little clearer when seeing what the code is doing.


First lets create and position the top-left and top-right corner pieces. As these aren't top be stretched, we treat them like any normal texture and position them on our frame accordingly.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
local TopLeft = CreateUIComponent("Texture", "TopLeftTexture", "WindowFrame")
TopLeft:SetSize(32,32)
TopLeft:SetTexture("interface/addons/AutoStretch/left-top")
TopLeft:SetTexCoord(0, 1, 0, 1)
TopLeft:SetColor(1, 1, 1)
TopLeft:SetAlpha(1)
TopLeft:SetAlphaMode("BLEND")
TopLeft:ClearAllAnchors()
TopLeft:SetAnchor("TOPLEFT", "TOPLEFT", "WindowFrame", 0, 0)
WindowFrame:SetLayers(2, TopLeft)


local TopRight = CreateUIComponent("Texture", "TopRightTexture", "WindowFrame")
TopRight:SetSize(32,32)
TopRight:SetTexture("interface/addons/AutoStretch/right-top")
TopRight:SetTexCoord(0, 1, 0, 1)
TopRight:SetColor(1, 1, 1)
TopRight:SetAlpha(1)
TopRight:SetAlphaMode("BLEND")
TopRight:ClearAllAnchors()
TopRight:SetAnchor("TOPRIGHT", "TOPRIGHT", "WindowFrame", 0, 0)
WindowFrame:SetLayers(2, TopRight)

Note how the top-right texture is being anchored here. Also note how these textures have a size defined for them. Again, this is done so that these textures have a fixed size.


Now to set the middle part of the title bar. This texture needs to stretch horizontally to fill the entire gap. There are a few ways we could do this, but as the two corner pieces have a static size, we can use both these corner pieces to anchor our middle section. The most convenient way is to anchor the top-left corner of the middle texture with the top-right corner of our TopLeft texture, and anchor the bottom-right corner of the middle texture to the bottom-left corner of the TopRight texture. This will insure that our middle texture fills the entire gap and since the corner pieces will not resize, the height of the middle section will not change. Here's th code:

Source code

1
2
3
4
5
6
7
8
9
10
local Top = CreateUIComponent("Texture", "TopTexture", "WindowFrame")
Top:SetTexture("interface/addons/AutoStretch/top")
Top:SetTexCoord(0, 1, 0, 1)
Top:SetColor(1, 1, 1)
Top:SetAlpha(1)
Top:SetAlphaMode("BLEND")
Top:ClearAllAnchors()
Top:SetAnchor("TOPLEFT", "TOPRIGHT", TopLeft, 0, 0)
Top:SetAnchor("BOTTOMRIGHT", "BOTTOMLEFT", TopRight, 0, 0)
WindowFrame:SetLayers(2, Top)

Note how the anchors are defined here and take a moment to think about the relation between these textures.


Try the code out at this point to see the effect. Note how the middle texture is being stretched.


To see this automatic resizong of the texture in action, try typing this following commands in the chat edit box while the window is currently being displayed.

Source code

1
2
3
4
/run WindowFrame:SetSize(200,400)


/run WindowFrame:SetSize(500,200)





Adding the Bottom and Sides
The bottom and sides of our example window can be added in much the same fashion. First we set the bottom corner pieces, then strech the bottom and sides to fit. Here's the code:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
local BottomLeft = CreateUIComponent("Texture", "BottomLeftTexture", "WindowFrame")
BottomLeft:SetSize(32,32)
BottomLeft:SetTexture("interface/addons/AutoStretch/left-bot")
BottomLeft:SetTexCoord(0, 1, 0, 1)
BottomLeft:SetColor(1, 1, 1)
BottomLeft:SetAlpha(1)
BottomLeft:SetAlphaMode("BLEND")
BottomLeft:ClearAllAnchors()
BottomLeft:SetAnchor("BOTTOMLEFT", "BOTTOMLEFT", "WindowFrame", 0, 0)
WindowFrame:SetLayers(2, BottomLeft)


local BottomRight = CreateUIComponent("Texture", "BottomRightTexture", "WindowFrame")
BottomRight:SetSize(32,32)
BottomRight:SetTexture("interface/addons/AutoStretch/right-bot")
BottomRight:SetTexCoord(0, 1, 0, 1)
BottomRight:SetColor(1, 1, 1)
BottomRight:SetAlpha(1)
BottomRight:SetAlphaMode("BLEND")
BottomRight:ClearAllAnchors()
BottomRight:SetAnchor("BOTTOMRIGHT", "BOTTOMRIGHT", "WindowFrame", 0, 0)
WindowFrame:SetLayers(2, BottomRight)


local Bottom = CreateUIComponent("Texture", "BottomTexture", "WindowFrame")
Bottom:SetTexture("interface/addons/AutoStretch/bottom")
Bottom:SetTexCoord(0, 1, 0, 1)
Bottom:SetColor(1, 1, 1)
Bottom:SetAlpha(1)
Bottom:SetAlphaMode("BLEND")
Bottom:ClearAllAnchors()
Bottom:SetAnchor("TOPLEFT", "TOPRIGHT", BottomLeft, 0, 0)
Bottom:SetAnchor("BOTTOMRIGHT", "BOTTOMLEFT", BottomRight, 0, 0)
WindowFrame:SetLayers(2, Bottom)


local Left = CreateUIComponent("Texture", "LeftTexture", "WindowFrame")
Left:SetTexture("interface/addons/AutoStretch/Left")
Left:SetTexCoord(0, 1, 0, 1)
Left:SetColor(1, 1, 1)
Left:SetAlpha(1)
Left:SetAlphaMode("BLEND")
Left:ClearAllAnchors()
Left:SetAnchor("TOPLEFT", "BOTTOMLEFT", TopLeft, 0, 0)
Left:SetAnchor("BOTTOMRIGHT", "TOPRIGHT", BottomLeft, 0, 0)
WindowFrame:SetLayers(2, Left)


local Right = CreateUIComponent("Texture", "RightTexture", "WindowFrame")
Right:SetTexture("interface/addons/AutoStretch/right")
Right:SetTexCoord(0, 1, 0, 1)
Right:SetColor(1, 1, 1)
Right:SetAlpha(1)
Right:SetAlphaMode("BLEND")
Right:ClearAllAnchors()
Right:SetAnchor("TOPLEFT", "BOTTOMLEFT", TopRight, 0, 0)
Right:SetAnchor("BOTTOMRIGHT", "TOPRIGHT", BottomRight, 0, 0)
WindowFrame:SetLayers(2, Right)

Take some time to think how the anchors are being applied here, especially for the left and right sides.


Try the code out now, and try those window resizing commands again.




Adding the Window Background
Our window is almost complete, we just need a background for it. Since the window frame textures have transparency in them and the corners pieces are also rounded, we can't just anchor them to existing textures, we'll need to add offsets as well. As this is a background, we'll also set this texture to a layer below the others so that it is drawn behind the other textures instead of in front of them.


We can either anchor the background to the corners of the frame itself, but I prefer to still anchor them to the textures, but also ensuring the offsets match. For the left side of the background, we'll offset 12 pixels in from the left of the TopLeft texture, and go up two pixels from the bottom of this same texture.


For the right side, we'll reverse these direction of these values based on the bottom-right of the BottomRight texture. and then backup 12 pixels horinzontally, and go up 12 pixels. This should then make the background texture fit nicely inside the window yet still cover the entire middle part. Here's the code:

Source code

1
2
3
4
5
6
7
8
9
10
local Background = CreateUIComponent("Texture", "BackgroundTexture", "WindowFrame")
Background:SetTexture("interface/addons/AutoStretch/bgrnd")
Background:SetTexCoord(0, 1, 0, 1)
Background:SetColor(1, 1, 1)
Background:SetAlpha(1)
Background:SetAlphaMode("BLEND")
Background:ClearAllAnchors()
Background:SetAnchor("TOPLEFT", "BOTTOMLEFT", TopLeft, 12, -2)
Background:SetAnchor("BOTTOMRIGHT", "BOTTOMRIGHT", BottomRight, -12, -12)
WindowFrame:SetLayers(1, Background)

Once again, try the code out and use those window resizing commands to see how the textures resize themselves automatically.




Other Uses of this Technique
With creative use of the multiple anchor points, we can have textures that stretch to fit different screen sizes. But the technique isn't limited to textures. Frames, buttons, sliders, and other widgets all have the ability to have multiple anchor points and therefore have the potential of auto-stretching to fit your needs.


For example, if you had a window with a scroll bar on the side, the scroll bar could be auto-stretched so that if the window was resized, the scroll bar resizes with it. A slider could be stretched to fit within a given window, a frame with buttons could stretch to accomodate more buttons, etc.


A little creativity and thought coupled with this technique has the potential to create some nice interfaces.

Woot... Finally got some sort of working attachments again, so here's the zip after all
Peryl has attached the following file:
2013... The year from hell....