Placeholder Image

字幕列表 影片播放

  • hello.

  • In this video, I wanted to create an easy to use men.

  • Ewing system on not just easy to use for the player, but also easy to use for me, the programmer.

  • And even though that might not sound like the most interesting of ideas, it turns out that the implementation of such a thing we'll use several c++ tricks that we've not seen on the channel before.

  • And I think it turned out to be quite interesting on the solution is quite elegant.

  • But before we start, let's take a look at what I'm building.

  • So the menu looks like this little pop up menu, and it's probably familiar thio people that enjoy retro games of a certain role playing game franchise.

  • And the idea is the following.

  • You have a list off commands that you can choose on.

  • You can see here that the list could be scroll up and down.

  • So the panel of the menu is a fixed site on Aiken Select.

  • One of the entries on a separate sub menu pops up on.

  • I can select one of those entries on.

  • We see a table of items that can be chosen, too, so the items within the panels do not need to be listed in just a vertical order.

  • A table can be created and we can see we can also block out certain items from being chosen.

  • The school bus here, on the right hand side of the panel, are in some way intelligence.

  • So they inform the user whether there are options below or above the current mouse cursor.

  • At any point, I compress the back button on to choose a different menu.

  • So let's choose Ah, white magic.

  • This time, when we get a different set of options, I can't move the cursor toe options that don't exist.

  • And it turned out you can also block off even sub menu.

  • So it's basically a hierarchical tree structure where we consent the properties that each branch and leaf of the tree.

  • When I do select an option such as the attack, there is no sub menu, so the system it elicits a small event.

  • I can go and do something with that event later on in my program.

  • In this case, it's got an I D.

  • 101 on.

  • I can see the name of it, too.

  • I'm going to pop the menu back up again, and this time the institute Black on will choose Bio to here so you can see the events appear in a nice, consistent way.

  • In many ways, this could be regarded as a graphical user interface on dhe.

  • Unlike many graphical user interface technologies, I wanted to build it in such a way that it was easy for me to use and to demonstrate this rather oddly, I'm going to look at some of the final code before we start programming.

  • Fundamentally, the system contained two objects.

  • The menu object on the menu manager.

  • The menu object is the tree.

  • We can access the levels of the tree by their own name, so Maine is the overall menu.

  • This is the attacks of menu.

  • This is the magics of menu on.

  • If we wanted further sub menus, we simply just make them exist.

  • To hear I created the White Magic menu on rather than typing this out each time, I can create a quick reference to that location on.

  • Then add in the sub items.

  • The panel sizes are in some way intelligence about what size they need to be, but they contain a two dimensional table.

  • So the top level menu, if you remember, was just a linear vertical list.

  • So it's got one column of entries.

  • But four visible rows of entries on this allows me to control the size of the menu, depending on where I want to place it on the screen.

  • In contrast, the magic menus are a bit larger.

  • So here I'm setting.

  • The tables have three columns across and six visible rose down.

  • Now, even though I've specified a two dimensional table, I still add to the items to the menu in a sequential order.

  • I can update some of the properties of the items simply by calling those properties while Stan creating the item.

  • And in fact, I could do this at any point after the item has been created.

  • So I can change the states of the item in real time, depending on the conditions of the game.

  • So if you recall in the Black Magic menu there were two options which were disabled, you see this set I d everywhere.

  • That's actually an optional extra.

  • I could very well use the string identify to determine what event has occurred, but sometimes it's easier to work with ideas in your program so wanted both options available, so this is really an optional extra.

  • In fact, anything like this is an optional extra.

  • Once I've constructed the full menu and then build it and that goes and recursive, Lee constructs all of the panels to be the right size and sets off the layout and makes a few intelligent decisions about what to display on.

  • I tell the menu manager you're going to work with this menu object, so the menu object is the tree.

  • The menu manager is the user interface to the tree, and here I've linked to user interface to that menu manager.

  • So as the user presses, the arrow keys were going to navigate around the menu system when they press the space key.

  • That's going to be a confirmed action, and several things might happen when you press the space key.

  • If there's a sub menu available, it'll pop up the sub menu.

  • If there isn't, it will assume that we're at the leaf of the tree, and therefore what we've selected is an action, and it will return the menu object associated with that location.

  • We can also press the back button.

  • And so if they returned, command object is not no pointer.

  • Some sort of user action has occurred, and we can interrogate that menu object and perform the relevant action as necessary.

  • The menu is also responsible for drawing itself.

  • In fact, all I need to do is call this draw function off the menu manager.

  • It feels a little unusual looking at the implementation code at the start of the video, but I wanted to put into context what we're really trying to do here.

  • I want to use a navigable, intuitive menu.

  • It's also very easy for the programmer to use, and you should compare this to regular graphical user interface frameworks on.

  • In fact, it just so happens I have one.

  • I've not made a video about this before, nor have I release the source code for it.

  • But it is actually a working and flexible windowing systems for the pixel game engine.

  • So we've got windows that can be selected and moved around.

  • We can choose the individual controls, got controls that highlight on mouse cursor over on things such as text entry on different dialogs can be selected two, for example, in this case, open a file.

  • This is a far more sophisticated graphical user interface framework, but you pay the price for that sophistication because the implementation of it is also quite sophisticated.

  • It's highly object oriented where we create one layer on, add other similar layers to it at certain locations.

  • And then we have an event system which passes around all of the different event that the user can throw at it.

  • And then we need to build the dialogues and the frameworks in order to be shown.

  • So all of this is actually concealed away in a pixel game engine extension, as I've mentioned not yet released and we can see the construction off the dialogue.

  • So this was the dialogue with the Czech buttons, and the test buttons on is a little bit more complicated.

  • We need to choose a particular component type, set its properties, set its location and then added to a parent window on all of these controls.

  • Just Azzan, WX widgets and Q T.

  • These air also eligible to be parent windows so we can customize things at quite a fine level.

  • On this sort of approach is great for Windows based interfaces, so if you've got a mouse and lots of options, which are going to be fiddly and lots of men using you need buttons and scroll bars and lists and all that sort of thing perfect.

  • But if you just want a quick pop up contextual menu in your game, then this object oriented approach is considerably overkill.

  • Now I know to some, the concept of a full, integrated, gooey framework would be a really exciting video, and maybe I'll make a video about that in the future.

  • But in this instance, I wanted something that was simple and quick and easy to use.

  • And this this entire menu system could be put on top of whatever you're rendering in the background, for example, a battle scene.

  • And quite importantly, I feel it doesn't require the use of the mouse, so we could also tie in some controller input quite easily.

  • So let's get started.

  • But just before we do, I wanted to bring your attention to a small community endeavor.

  • It's the community 0.1 loan coded dot com blawg on.

  • The idea is that members of the Discord community analyst YouTube community can write technical articles about programming technology game reviews.

  • Whatever that interests us is a community for others to see on.

  • It's early days yet, so there's not a great many articles here, but there are some already particularly talking about how to use the pixel game engine in certain ways.

  • This one, for example, is looking at how we can explore binary mathematics on the rules so far is that anything that's interesting to a member of the community is eligible to be included on the platform.

  • So if you're feeling creative on, want to write for a platform that people do actually visit and we'll see the articles, then shout out on the discord server for now, and we'll get you settled now back to the menu ING system.

  • We know that the manual system is going to consist off panels, and each panel is going to contain a table on the table consists of a certain number of columns and a certain number of visible rose.

  • So in this case, our table is two by four.

  • Candle itself is a menu object on the table.

  • Cells also contain menu objects, and as I add more menu objects to the table, the table gets populated in the following order.

  • If we've got more objects than we have visible cells, then you can conceptually assume these air being added below.

  • Now let's suppose I take any one of these men you objects.

  • Let's take D here.

  • I can specify that De is also going to have some Children, and I don't need to specify that explicitly.

  • I just add Children to it in the same way I've added a through N here, and I do apologize.

  • If there was some issues with the sound at the moment, there is quite a storm raging.

  • Whilst I'm recording this video, it's a shame that forthis many objects my table is going to be one comma five.

  • So it's only going to have one column of entries, but it's going to have five visible rose and again as we add items to them.

  • The same story applies because D in this menu object is a menu object that has Children.

  • We can identify it as being the title off.

  • Some sober menu that's going to appear on will do that by putting a small red icon pointing to the right.

  • So when the user moves the cursor to the D location and presses.

  • The action button will pop up the sub menu belonging Toa Item D, and we can continue constructing our tree using this approach with his many levels as we like, because menu objects can have a whole set of properties associated with them.

  • And in this video, I'm really looking at a very simple one, such as the ability to be enabled and disabled.

  • We can change how we draw the menu object in the table cell.

  • Now, don't forget.

  • The primary goal is to come up with a system which is easy for me to use on your notice.

  • We've not specified the dimensions of anything other than the sizes of the tables we want in our menus.

  • Each menu object is able to return the dimensions off the space it requires to be viewed.

  • Now, in my simple menu system, all we're looking at is one line of text.

  • But looking at this menu, for example, how do we determine how wide it needs to be?

  • Well, suppose we started to add items to it, said well at Cat Dog orange.

  • I want to ensure that the largest command in the list is fully visible.

  • And so when we construct the menu object, it will look at its Children in order to work out which is the largest and use that dimension to influence the size of the panel.

  • The same thing happens in a two dimensional table.

  • In this case, orange is still our largest entry, so all of our cell wits will be able to accommodate the word orange.

  • Captain Dog will just be left justified on this gives a pleasing visual aesthetic of order.

  • A table cell will naturally be the same size as the largest object within the menu object.

  • But you might not want to draw them old right next to each other.

  • You're going to need some space around them, too.

  • On I called this Paddick, which is how much space do we want around our table cells.

  • To construct our menu, we effectively build a tree off nothing but menu objects.

  • So a menu object can have many child menu objects, which can have child menu objects, which can have child menu objects, et cetera, et cetera.

  • Each menu object is given a name, so in this case, our root menu is Maine, and it's got too many objects which don't have any further Children attack and defend.

  • But it does have one that has two Children magic, so we'll make sure that magic is drawn with our little sub menu indicator effectively.

  • The names highlighted are the titles of the menu object, and we differentiate between the menu object being a command or a Superman you based on whether or not it is a leaf of this structure.

  • And so the leaves we have here are attack and defend, but also ice fire on our white magic spells to when the user presses confirm over one of these, we can effectively fire an event back to the host system.

  • When a menu object is selected, that's not a leaf.

  • Then we'll open up the child menu.

  • So I think we've established now that all of the data is menu objects.

  • But to manage the cursor and the drawing of the menus, we have a menu manager because this is a tree.

  • There is only one singular path from this item through to this item here, and I want to display all of the menus that we've taken to get to that path.

  • And so the menu manager will maintain a sort off stack where our root menu system it is always at the bottom of the stack on the menu currently being interacted with the user is at the top of the stack.

  • The menu objects themselves are responsible for positioning a cursor.

  • And so when the user presses the up, down left, right and action keys, we just forward those events on to whatever is at the top of the stack.

  • In this case, we've got a single one column list, so the left and right keys aren't really going to do anything.

  • But the up and down case we'll move our cursor accordingly when the user presses the back key or we need to do is pop whatever is off the top of the stack.

  • And naturally, our focus returns to the menu object, which is now at the top of the stack.

  • We should have been the preceding menu object, so because we only have this singular path through to a final action, a Stack makes a really nice data structure to maintain our rendering order on dhe deduce where we need to send the event to in this two dimensional table menu Of course, the left and right keys now matter so the cursor can be moved anywhere within this table.

  • But if they are here on, they press the action button.

  • We know that because this particular cell happens to have some Children, we return to the menu manager.

  • A new menu object to put on the top of its stack, and therefore it opens up this menu object I mentioned a few minutes ago that thes panels are actually windows into infinitely long tables, tables with an infinite number of rows.

  • By maintaining what is our top visible row, we can determine if we need to place indicators to the user.

  • That's the menu can effectively be scroll vertically.

  • In this case, if there are more entered rose in the menu objects than a visible, then yes, we do need to display some sort of indication.

  • If they use a house scroll down a bit.

  • We can use this top visible row indicator on the dimensions of the table behind the menu.

  • Object to determine these indicators in both directions.

  • I want the rendering of the panels to be in some way skin herbal.

  • I don't want them to always look the same.

  • So I'm going to use the nine patch approach to determine what's the boundary and contents of a panel should look like now.

  • This approach is quite commonly seen on systems where the controls may have variable sizes, and in principle it allows you to specify the four corners, the horizontal boundaries, the vertical boundaries onto the Phil off a rectangular shape on the screen of any dimension.

  • So, for example, I have a single sprite, which contains the information I need to render my panel and it split up into nine patches.

  • It just so happens that my patches happen to be eight pixels by eight pixels on.

  • I've chosen this for a reason.

  • It's because the built in front for the pixel game engine happens to be eight by eight pixels.

  • And so, by working in patch space, I could make sure everything is neat and tidy without having to do lots of cumbersome calculations.

  • So in this nine patch layout of my top left corner, which looks something like this on my bottom right corner, that's bottom left that one's button right on top, right?

  • So I got my four corners and then also specify my top boundary, my bottom boundary, the left boundary on the right boundary.

  • These could be graphically quite nice, and I'll show you mine in a minute.

  • But then I'll fill in everything else, and you notice all fill in this center cell that just with a plain background color needn't be playing.

  • It could be a texture that matches up with the cells either side of it to maintain a nice, curved corner aesthetic.

  • Regardless of what's in the background.

  • I've actually set these areas too transparent, and in fact, here is the sprite that I'm going to use.

  • So it's very small pixel art base Sprite on.

  • We can see my nine patches, so these are all eight by eight cells, which I'll creatively snap out of this spite in order to construct the final panel window.

  • I've also included some overlays for the indicators.

  • For more items open down on the sub menu on have included a two by two patch.

  • This approach allows me to change the look of the man Ewing system entirely, even run time, if necessary.

  • I could even have animated panels on just change the Sprite over time.

  • The menu system doesn't care.

  • All it cares about is the location of these primary objects in this source Sprite.

  • I'm almost done with the slides now, and I'm going to go through the code quite quickly for this video simply because a lot of it is the calculations of drawing positions.

  • It's not very interesting, but there is one facet I do want to talk about, and that is how do we store the data in principle, I'm going to use a standard map on We've seen standard map on the channel before in the code it yourself role playing game, Siri's effectively.

  • It allows you to store keys and value pairs.

  • So in a very pseudo conceptual level, you could consider, for example, map Apple equals one map.

  • Orange equals two.

  • And yes, I really do mean we can use a string is the identify so it looks like an array.

  • But we can effectively use another object as the index to that array, rather than just a new miracle index what you would with a normal array or director.

  • But in my map, instead of just pointing to a basic primitive type like that, I'm going to store a menu object on all of my menu.

  • Objects are going to have a map that holds the Children.

  • So taking a menu object, I could look at it's associative map passing the name off some supper menu object, which would return a new menu object so I could look at its map and look at the sub menu.

  • Objects returned there on so forth.

  • So I got this sort of recurrent data structure on by overloading the array access operator.

  • I can actually get rid of this bit, which means I can access a specific menu object in an easy to use and very clear syntax manner on.

  • I'm going to start out by doing it like this, but will quickly see there is a problem doing it this way.

  • But let's start writing some code.

  • As usual, I'm going to use the OLC pixel game engine, and here I just got a blank pixel game engine project.

  • If you want to see how to create one of these in Visual Studio 2019 then click.

  • The little Link of Bob made a video explicitly.

  • But how to set up one of these projects in that environment on all this program does so far is, it decides what the patch size is going to be, which is going to be eight by eight pixels on it includes standard map.

  • It also then goes and loads thestreet asset that I need to draw the menus and his views of this channel.

  • No, I love the retro aesthetics.

  • I'm using quite large pixels this time, but to make it look Pixley so therefore, by four screen pixels per game pixel, we know that we're going to need a menu object class.

  • So let's throw that in straight away.

  • And in this menu object class, I'm going to need a bunch of description.

  • Variables.

  • What is its name going to bay?

  • Can you select the object?

  • I'm also going to associate an I D with the object.

  • This might just make it easier for filtering out the events later, also going to store the number of rows that make up the table onto partner.

  • That I went, how a variable, top visible row were in the table.

  • Do we start drawing from Since we know there's a table, let's start some properties off the visible table, so I'm going to use the Vector two D interview type that comes with the pixel game engine to do this.

  • So this is currently one column wide zero rose.

  • Hi, I don't yet know what the cell size in the table needs to be.

  • I will calculate that later.

  • We also know that the cells can have some padding around them.

  • In this case, I only want to pad in the X axis, and it's going to be two patches worth of padding between cells, since the entire system relies on rendering in the spatial domain off patches thes eight by eight pixel squares I'm going to store How big is a patch, which is just a two d vector containing eight by eight?

  • But I also need to know the size of this if it becomes a panel in patches.

  • Finally, and most importantly, we're also going to need our map on the maps going to take in a string.

  • A CZ.

  • The key value but store menu objects is going to represent the Children I appreciate.

  • This might seem like a lot of data to immediately get started with, but nearly all of it will be self calculated by the object.

  • Don't forget the objective of this exercise is to make code that is easy to use.

  • I'm not going to throw in some access of methods now.

  • In the past, I've made fun of using getters and setters, but they do sometimes serve a purpose.

  • I want the code to be easy to use, so I don't want intel a sense or the equivalent on other platforms.

  • It's a pop up with suggestions which aren't the correct ones for me to use.

  • So I'm adding in two constructors that set the name of the menu.

  • Object here have added a method set table which sets the table dimensions of the object.

  • Now, interestingly, it returns appointed to itself on.

  • We'll see why this is important.

  • Later, I'm also going to have one which allows me to set the I D.

  • And again it returns a pointer to itself.

  • The same applies to an enable method, So I just added a few getters in there as well.

  • Now here I'm going to add a function which returns the size in patches of this particular menu object.

  • This is not the same as the size of the panel.

  • This is the size required to display the name of the menu objects, so in this case, it's simply the length of the string that represents the name on one.

  • If we wanted to develop this man Ewing system further, where we had different types off cells, for example, the cells could contain icons, OSS, Progress Bar's or something.

  • We could change the size of the cell space required to display this menu object here.

  • I also need to know whether this particular menu objects has lots of Children.

  • So I'm just going to interrogate the state of our map.

  • And finally, I'm going to overload the array access operator to just make accessing the map a little easier.

  • Let me just show you how this works.

  • I've got some men.

  • You object Now I can create just a simple menu.

  • For example, magic.

  • We know that this array access operator has now gone into our map.

  • Now, when you use a map and you use a key that it's not seen before, it will create a new menu object and associate it with that key.

  • So even though we've not explicitly created any sub menus, I can keep going.

  • This will internally construct the three men you objects required and link them together.

  • I can then choose which properties of this final object I want to set.

  • So set the I d say it's 101 but because the set I d Function also returns a reference to that object, I can set the other properties, too.

  • Now, if you're just starting out with C plus plus, this approach may look quite strange, but it does allow me to construct menus quickly with all of the information that I need without needing to store lots of in between linkages.

  • In this one line of code, we've now created three menu objects that are linked together on.

  • We relied on the standard map function to do that generation for us, and I'm quite happy for that to be the case because it means now I can add further sub objects to one of the in between stages on it will semantically make sense In this first line, this black sub menu was created, so for this second line, it's already exists.

  • It knows which menu to access.

  • To add to the ice object to going back to our many objects, I'm going to want a method for it to draw itself.

  • You'll notice that the menu object is external to the pixel game engine, and it doesn't inherit from the pixel game engine, either.

  • So in order to access the pixel game engines drawing routines only to pass in a reference to the pixel game engine when I want to draw the menu, I'm also going to pass in.

  • A pointed to the Sprite, which contains the nine patch on the two d vector, were to draw it on the screen.

  • Fundamentally, the drawer self method is only going to be called if this menu object is indeed a panel, I It was an object that has some Children, but we don't know what the dimensions of that panel are just yet, and I don't want to calculate it every single frame.

  • I can calculate all of these fixed dimensions once I've finished constructing my menu.

  • So I'll add to this build function, which populates these variables down here with the relevant sizes for when the object is drawn.

  • Now I go on to have to call the build function for every single men you object.

  • There's absolutely no need to our menu.

  • Objects are stored in a tree so we can recursive Lee navigate that tree hierarchy as long as we only call the build menu on a root menu object.

  • And so that's exactly what I'm going to do.

  • It iterated through all of the Children in my Items map.

  • If there are no Children, this situation wrote occur.

  • If one of my Children happens to have Children of its own, then I'm going to call the build function on that child.

  • And so this will didn't recursive Lee navigate that whole tree?

  • But the object returned from a map.

  • When you're iterating through it is a standard pair where the first element of the pair is the name.

  • Either key value on the second element is the actual object.

  • So for now, I want to tell it to use the menu object component of the item returned from the map.

  • Now things get a bit strange when we need to start thinking recursive Lee.

  • Whilst I'm iterating through all of these Children, I may as well at the same time determine what cell size I need for myself.

  • On recall that cell size is related to the string that identifies the menu object by Iterating through them all on retaining the maximum dimensions.

  • Then I get the largest cell size that I need to satisfactorily display all of my Children.

  • Knowing this, I can calculate the size of myself in patches on.

  • We'll see a bit of code that looks like this, and I'm not going to go into too much detail explaining it because it's got some magic numbers in.

  • But these are just aesthetics for drawing.

  • In this case, I'm taking the dimensions of the table, multiplying it by the maximum cell size has been returned.

  • But then I'm also taking into account that padding between the cells.

  • Now one of the metrics I do need to record for this particular menu object is how many rows are in its table.

  • I know the number of Children I have because it's the size of my map.

  • From that I can calculate how many rows I need by dividing it by the number of columns that I have.

  • However, if there is any remainder from this division, then I need an additional row to display those remaining items on.

  • I can work out if I've got a remainder by taking the module o of the number of columns on this turn, every operation is testing to see.

  • Is there any remainder if there is at one more row or don't so using this build function, we now have enough information that if in the events this menu object has ever displayed as a panel, we know how to draw it.

  • So let's go back to the drawer cell function and do just that.

  • I know that my nine patch spite is going to have some transparency, so I'm going to cash the current transparency mode of the pixel game engine.

  • The user could have set it to something they need.

  • I don't want my menu to silently change the state of the pixel game engine and therefore, before we exit dysfunction, I'm going to restore the current pixel mode.

  • The user will never know the difference.

  • Since we now know the size of the panel in patches, we can draw it just with a perv nested four loops.

  • So here I've got one for the X axis on one for the Y axis on.

  • We're going to go through patch by patch, decide which patch out of our nine patch Spite do we need and draw it on.

  • I'm using the pixel game engines draw partial Sprite function to effectively cut out the patch from the source Sprite and draw us in the right location.

  • Let's test it at this stage.

  • So in on user create, I've created a very small menu system here.

  • It's only one deep.

  • It's a small table, which is one column by 10 rows, and I've added five items to it.

  • So the panel is actually to be larger than the number of items on the end.

  • I've called the build function on the menu object so it will have gone through and calculated the size is needed to display the Children.

  • And for now, in on news update, I'm calling the drawer cell function directly on the main panel, passing in the pixel game engine on the Sprite.

  • Before I draw the menu.

  • However, I'm just going to throw in a very quick hack to show the limitation about using standard map, and it's down here, have thrown it in the drawer cell function on.

  • All I'm going to do is draw the names of the men you objects of the Children to.

  • This particular panel So I'm Iterating through all of the Children, working out where on the screen they should be displayed on, then drawing the name, the key value of that menu object.

  • Let's just take a quick look.

  • Forget for a moment that the panels the wrong size.

  • What we see is that the order of the items is not what I specified.

  • Here.

  • We've gotten attack, defend escape items and magic.

  • But in my code, I've got attack magic Defense Item Escape.

  • I want my men used to be constructed in the order that I construct them simply because it is customary to put the most popular options at the start of your menu so the user doesn't have to navigate them as much.

  • In fact, what we see in Game is that the items of the menu object are in alphabetical order, and that's because map intrinsically sort by key value.

  • And so even though map has given us this fantastic level of flexibility, it's actually letting us down when it comes to the ordering of the items.

  • Fortunately, there is a way around this where we can maintain the flexibility but also at order to our map and rather Oddly, I'm going to change my map from a map to an unknown ordered Mack, which sounds counterintuitive, But the reasoning for this is this follows a standard map, is actually a very sophisticated data structure and has a certain degree of computational complexity when using it.

  • So even though it's easy for us to use, the computer has to do a lot of work in order to maintain the map.

  • Since we don't care about the order of things in the map being maintained by standard map, I might as well use a standard on ordered map, which is slightly more computational e efficient.

  • However, as the name implies, this will not enforce any ordering it all.

  • So it's still not solved.

  • The initial problem.

  • I want my objects to appear in the order that I create them, so I need to store them in a data structure that maintains an order like that.

  • Fortunately, we have standard vector for that job, So I'm going to store my menu, objects in a standard vector and call these items.

  • But I can't access the vector like I did with my map instead.

  • What I'm going to use my map for is to store the indexes into the vector.

  • So I map is now going to be a map that links names of menus to introduce, and those interviews are going to be the indexes into my vector.

  • This approach gives me the best of both worlds.

  • My vector is computational e simple toe work with, so we don't really get any performance penalty whilst using the menu.

  • It also maintains the order of things in which I created them so aesthetically it's going to look like how I want it to look.

  • But I like the convenience of using the map access syntax in order to define the structure of my tree.

  • This change requires us to just change.

  • A couple of other locations are ray.

  • Index overload is no longer as valid as it should be, but I wanted to behave in the same way, So the first thing I'll do is check in my map of item pointers.

  • Is there an item that already exists with this name?

  • If there isn't there, I'm going to create it.

  • So this is linking the name to the current index of the vector by taking the maximum size of the vector we know what the next index is going to be, and then I'm going to push to that vector the newly constructed menu object item on here.

  • You can see I'm using the constructor where I associate the name of the object with the object.

  • So now items being a vector.

  • We can't use this string to index it, but we can use the pointer returned by looking at the map that links the names with the pointers.

  • It's all good stuff.

  • This isn't it.

  • So, in effect, this function will behave exactly as it did before.

  • But it will allow us to maintain the ordering of the items because items is no longer a map, there are a couple of other changes will need to make two.

  • In my little bit of hacked up test code, we don't access first and second members of a purr anymore.

  • The victor will just return the object completely.

  • The same applies in our build function.

  • We're no longer working directly with a map.

  • We're working with a factor so nicely.

  • This is simplifying a lot of the coat.

  • Note that I'm not making any changes to the construction, so let's take a look now, So now we can see that the ordering of the words is as we intended them to bay.

  • You may have noticed something else.

  • They now all fits within the menu panel.

  • Why is this?

  • We know that a cell gets its size from calling the menu objects get size function, which is based on the S named string.

  • Well, in the previous bit of code where we had together the map, we never constructed the object with a name.

  • So the name of all of the objects happen to be route.

  • Now that we know that the size of the panel is related to the largest object, we contest this principle.

  • So let's say I change.

  • One of these to defend is now the largest and press play the menu has adapted to accommodate the size of that item.

  • From a programming perspective, this menu is now getting very simple to use.

  • Indeed, I'm not going to remove our hacked bit of code on put in the proper code for displaying the contents of a menu object.

  • We've drawn the panel in the background.

  • It's now time to draw the individual cells.

  • Since we're working with a two dimensional table, which don't forget, can be one dimensional in nature.

  • It's going to be useful for us to know what the top left visible item is on.

  • The bottom right visible item is because that will tell us which rose of the table we need to draw.

  • Bottom right is calculated by simply adding the table dimensions to the top left.

  • So this could in principle become larger than the number of child items this menu object house.

  • So I'm going to clamp that just to make sure we don't go out of bounds on our child Vector.

  • This means, given the size of the panel that we've specified through the table dimensions, I can work out how many visible items there are, regardless of whether it's a wandy list or a to D table, and this allows me then to iterated through all of the visible items in a linear fashion.

  • Many times on this channel, we've worked out the one D location based on a two d coordinate.

  • Its why times with plus X.

  • I must have said that over 100 times now, but this is a situation where we want the opposite were given a one D index, and we want to work out a to D Coordinate, Weaken do effectively the inverse where we can take our one d I and calculate our cell location, X and y This will effectively propagate through the table from the top, left along horizontally and then onto the next row all the way to the left, all the way to the right and the next road left to right until we run out of visible items.

  • If I know the cell location, I can work out where I want to start drawing.

  • So this is in patch space, and this includes the paddock.

  • Once I've worked out where I need to draw in patch space, I can work it out in screen space.

  • And since this is a panel and it's drawing the names of its menu objects as its Children, I'm going to call the drawstring function at the correct location.

  • I'm going to get the name of the item we're currently drawing on.

  • I'm also going to test is the item we're currently drawing enabled because if it is, I'm going to draw.

  • It is white, and if not, I'm going to draw it grayed out.

  • So let's take a look.

  • Well, it's certainly drawing in the right color.

  • I'm just going to modify our test there to make a little bit more sensible.

  • So I put that back down to defend.

  • But I'm also going to add a whole bunch of dummy items at the bottom, and this is so a contest to see if my table is being calculated correctly.

  • So we've now go.

  • 123456789 10 11 12 13 14 items.

  • But our table can only display 10 of them.

  • Nice.

  • It's adapted, and it's not displaying anything beyond the bottom of our table.

  • However, if I change our table dimensions now two and 10 potentially, the panel can display 20 items.

  • So here it is, displaying all of the items with some empty space at the bottom, but it's arranged them according to our table layout.

  • I change that to three by, say, five as a maximum number of rows to display again.

  • We see everything's laid out in a nice, orderly fashion.

  • The padding that I keep mentioning is this spacing between the columns.

  • We don't have any padding in the Y axis, though we could.

  • Let's try it.

  • Padding is currently fixed in its hard coded.

  • So if I change the padding here to give me one patch worth of padding between the items vertically, we can see a bigger table gets created.

  • Now.

  • Some of the complexity around that padding code is the handle.

  • The border around the edge.

  • I don't want to pad unnecessarily.

  • I just want to pat the space in between the items.

  • I'll put that back.

  • For now, there are a few cosmetic enhancements we need to make to drawing the panel.

  • We need to place the indicator markets to let the user know whether there are items above or below the panel or the item is indeed the header off another menu.

  • Once I've worked out which items are visible, I can use my top visible row indicator, which we've not updated yet.

  • But we will to determine which of the scroll markers are visible.

  • So if my top visible row is anything greater than zero, then obviously I can move the menu upwards.

  • And all this is doing is choosing the correct location on the screen, choosing the appropriate patch from my patch right and using the drawer partial Sprite to draw it in exactly the same way.

  • I can determine if I need to draw the bottom scroll marker and in a very similar way, when I'm drawing the Children, I can also determine if the child has Children of its own.

  • Then it is the head of a sub menu object, and so again, calculate the location in patch space, then screen space.

  • Choose the appropriate patch on draw it to the screen.

  • So now if I make sure that our menu cannot display all of the possible items that contained in the menu, we get a little indicator.

  • Marketers say there are more items below here.

  • It's difficult to test this without the cursor, so I think it's time to add that in.

  • It's important to remember that our menu object class is purely a data structure on.

  • I'm going to defer to a second class menu manager, the responsibility off, maintaining the curse of positions and which menus are drawn to the screen.

  • This is a very simple class, has a constructor that doesn't do anything, but it does maintain a list of panels.

  • Now I'm going to be using this list like a stack, and I'm not using a stack explicitly, because I need to access all of the elements in the stack.

  • Now Stack will only give you access to the top.

  • I need to look at all of the elements sequentially on this stack in order to draw them, because these are going to be the panels that are nested.

  • The menu manager is associated with a menu object bear on open function.

  • All the open function does is push the panel supplied onto the stack.

  • I'm also going to include a close function, which clears the stack.

  • The menu manager is also going to be responsible for drawing the panels.

  • So in much the same way, we pass in the pixel game engine and the sprite.

  • If there's no panels to draw, it doesn't do anything.

  • Otherwise, it it'll rates through all of the panels in the stack and draws them starting up with the root.

  • And so that way, it draws the panels on top of each other in the correct order.

  • Each time it goes to a new panel, I'm going to offset the top left corner by a little bit to indicate that they're being stacked.

  • One could spend some time making this farm or intelligence to ensure that the panel resides on the screen.

  • So if we have many menus with lots of depth, eventually we're going to come off the screen.

  • There's nothing stopping is adding some intelligence here to make sure the panel remains visible at all times.

  • The menu manager will also handle user input.

  • So, for example, if the on hope function is called, it takes whichever panel is on the top of the stack or, in this case, the back of the list and calls and on up function.

  • We've not implemented those just yet, but it demonstrates that by using a stack to maintain the panels, we always know which one is going to receive user input on.

  • We can handle all of the other cases exactly the same way on back.

  • All that's going to do is pop the top off the stack, which means whatever menu was under the current menu is now the top of the stack.

  • This effectively allows the user to go backwards, back up the hierarchy of menus as well as having a back.

  • But we're also going to have a confirmed button, but I'll leave the implementation of that too a bit later.

  • And so this structure implies that are many objects need to maintain their own cursor locations on this has the nice side effect that the curse location is persistent between menus.

  • So if you're always going to the same location in a particular sub menu, the cursor will appear there.

  • You could, of course, always reset the cursor as well, if that was the fact he wanted, since the menu object will now handle the cursor that sound in some variables to help it.

  • The first is a to D variable, which is which cell in the table is our curse are currently pointing at.

  • But don't forget that the cells in the table are effectively a one D array, even though they're displayed is to d.

  • So I'm also going to have the index equivalent of that location.

  • I'm also going to create a vector, which is the cursor in screen space, so the menu manager knows where to draw the sprite of the little hand pointing at the item.

  • So we know we need to add on up, left down and write functions to our menu objects.

  • I'm only really going to talk through on up because they're all quite similar.

  • Whenever the use of presses the AAP ki, we want to move our cursor up one cell in our table.

  • So I'm going to deck Ament the Y value of that variable.

  • We can't go beyond the boundaries of the table, so I'm going to check for that, too.

  • Now recall that we have a top visible row.

  • That's if our table is larger than the panel allows us to view.

  • If our curse that goes beyond the top visible row, then I want to deck Ament our top visible rope, so this will have the effect of shifting the entire table upwards or downwards depends on your perspective.

  • On likewise, we don't want our top visible row to go below zero, so handling the cursor in two D space is very simple, but I want to make sure that we don't end up pointing to a location that doesn't contain an item as you've seen before.

  • If we don't have enough items to fully fill the table there, empty spaces on those empty spaces avoid locations.

  • They could point to elements that don't exist in our Children vector.

  • And since this is a routine we're going to be using after every movement, I'm going to create a function called clamp cursor clam.

  • Kerson directly converts the two D cell coordinate of our curse a location into our one devalue so we can access it in the vector.

  • However, we want to make sure that that item is within the boundaries off the vector.

  • If it isn't, then I want to clamp it to the maximum size.

  • Now, if I clamp this value in the one dimensional domain, then I also need to clamp the curses location in two dimensions.

  • This will have the effect of forcing the cursor to go to the left most item on a row of items that isn't fully occupied.

  • I'II were assuring that we're not ever pointing at an item in R two d table that doesn't exist in our one d vector.

  • I feel this needs a bit of graphical clarification.

  • So here we've got our two d table.

  • But we know that our vector is one dimensional and it will populate the items as I'm doing on the screen right now.

  • The problem occurs in this situation where there are more table cells than the reflective elements.

  • I never want to be able to point to these locations because even though they exist in our table, they do not exist in our vector, which would allow us to go out of bounds effectively on the factor on down very similar to our up except the opposite direction left and right there, actually considerably simpler because they don't concern themselves with the top visible row of the table.

  • Now that the menu object maintains a cursor in two D space around its table, I also want to work out where the cursor is in screen space.

  • And I'm going to do that by piggybacking the drawer cell function because we only ever need to calculate that location if the menu has been drawn.

  • So at the end of this function, I'm going to calculate the screen space location off the cursor, and it looks like a whole bunch of maths.

  • But it's very simple effectively.

  • We take the two dimensional coordinate of our cell and converted into patch space, and then we offset it into screen space.

  • We do that for both X and why now the only thing that makes this look a little bit more on will be than it should be.

  • For example, having this minus patch and plus patch of the end is that the curses Sprite is 16 by 16.

  • But the finger points in the top half of the Sprite.

  • So want the top half of the spike to align with the item of text that it's pointing at.

  • I'll add to our menu object a quick getter just to return that cursor position.

  • So now in our menu manager class, we can interrogate whichever panel is at the top of the stack, because that's where our cursor will start.

  • Get the curse a location on Draw the Sprite on in a similar way before I'm just cashing the current pixel mode so we don't interrupt with whatever the user is currently trying to draw on dhe.

  • I cut out the hand sprite using the drawer partial Sprite function and give it the appropriate parameters to draw it on the screen.

  • Now that we have a menu manager class, it's time to give it control.

  • It's only necessary to use it in on user update.

  • So if the n key is pressed.

  • I'm going to open a particular menu object by giving it to the menu manager.

  • I'm then going to link the direction keys to the corresponding menu manager functions that I'm going to call the drawer, function off the menu manager to draw the system to take a look.

  • So nothing.

  • I'm going to press the M key on our menu has popped up.

  • We see a cursor.

  • It could be navigated around the menu.

  • And if I go beyond the bottom of the visible menu, it starts to scroll down.

  • Now just do that again.

  • So I'm only going to press down in this third column, but see how the curse and moved to the middle.

  • That was our clamp curse of function coming into effect because there was no item in that bottom right hand corner.

  • We can also now verify that the skull indicators are working quite nicely, too, so we've only got some finishing touches to add to this. 00:48:37.070 --> 00:48

hello.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

編程一個復古的彈出式菜單系統 (Programming a Retro Pop-Up Menu System)

  • 2 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字