PC Games
• Orb Tutorials
• 2D shoot 'em up Latest Updates
SDL2 Versus game tutorial
Download keys for SDL2 tutorials on itch.io
The Legend of Edgar 1.37
SDL2 Santa game tutorial 🎅
SDL2 Shooter 3 tutorial
Tags • android (3) • battle-for-the-solar-system (10) • blob-wars (10) • brexit (1) • code (6) • edgar (9) • games (43) • lasagne-monsters (1) • making-of (5) • match3 (1) • numberblocksonline (1) • orb (2) • site (1) • tanx (4) • three-guys (3) • three-guys-apocalypse (3) • tutorials (17) • water-closet (4) Books Alysha When her village is attacked and her friends and family are taken away to be sold as slaves, Alysha Tanner sets out on a quest across the world to track them down and return them home. Along the way, she is aided by the most unlikely of allies - the world's last remaining dragon. |
— Creating a basic widget system — Note: this tutorial assumes knowledge of C, as well as prior tutorials.
Introduction The final widget we're going to create is one that will allow us to configure controls. Being able to redefine the controls of a game is a very common desire, so we'll create a widget that allow us to set a value based on a key press. Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./widgets07 to run the code. You will see a window open like the one above. Use the Up and Down arrows on you keyboard to change the highlighted menu option. Select any of Left, Right, Up, Down, Fire, or Jump, and press Return or Space to edit the value. The value to change to ... (three dots). Press any key to set the new value, or press Escape to cancel. When you're done, either select Exit or close the window. Inspecting the code As usual, we're defining our Widget in JSON. For this tutorial, we've made a new JSON file: options.json. Our control widget looks like this: { "type" : "WT_CONTROL", "name" : "left", "x" : 0, "y" : 150, "label" : "Left" } Again, nothing we've not seen before. It only defines the basics. Once again, we've also added a new enum to defs.h:
WT_CONTROL will be the type of our control widget. The widget itself is defined in structs.h:
Again, not a lot to it, just x and y coordinates and a value. One other update we've made to structs.h is adding a new field to App:
We've added a field called lastKeyPressed, that will be used to track the last keyboard key that was pressed. We'll be using this when we come to update our ControlWidget. Now, let's look at widgets.c. As expected, all the major logic changes have gone into this file. Starting with initWidgets:
Alongside handleInputWidget, we've introduced a new variable called handleControlWidget. This will be used in our logic phase to tell the code that we're currently working with an InputWidget and want to disregard everything else. Our createWidget function has been tweaked handle WT_CONTROL:
For WT_CONTROL types, we'll be calling createControlWidget:
An easy function to understand. We're mallocing a ControlWidget and assigning it to the widget's data field. We're also calculating the widget's width and height, according to its label. Our doWidgets function is next. This function has gotten quite large now; any larger, and it might be worth splitting into subfunction, to make it easier to read. Let's look at what's changed:
To start with, we're now testing that both handleInputWidget and handleControlWidget are 0. Should they be (meaning we're not processing a text input widget or a control widget), we'll process the logic for our standard widgets. The change we're most interested in is if we press Space or Return when the activeWidget is WT_CONTROL. We're setting app.lastKeyPressed to -1 and handleControlWidget to 1. Setting handleControlWidget to 1 will mean that doWidgets will now call out to doControlWidget (according to the if-else control flow). doControlWidget is where we'll be processing the user input for setting a new control:
The first thing we'll do is test the value of app.lastKeyPressed. We'll do nothing if it's -1 (which is the value it is set to when we press Return or Space on the widget in doWidgets). If it's not -1, we'll further test that we've not pressed Escape. If not, we'll extract the ControlWidget from the widget's data field, and set its value to that of app.lastKeyPressed. With the contrl now updated, we'll call the widget's action function pointer, if one is set. Whether Escape was pressed or not, we'll then set handleControlWidget to 0, to restore normal widget logic flow, and then clear the key that was pressed (app.keyboard). This step is important to ensure our doWidgets flow doesn't immediately process the key, in case it was one of our control keys (arrow keys, Return, Space, etc). And that's all we need to do to accept key configuration for the widget. Rendering the ControlWidget is just as simple. First, we update drawWidgets:
We've added in a call to drawControlWidget for WT_CONTROL type widgets. drawControlWidget is quite similar to other widget rendering functions:
We're rendering widget in green or white, depending on whether it's the active widget. Next, we're rendering the label. After this, we're testing whether handleControlWidget is set and if our widget is the active one. If so, this means we're currently editing the widget's value. We'll reflect this by drawing ... (three dots) in place of its value, as a visual prompt to the user to press a key. If we're not editing the ControlWidget, we'll want to render the key it is set to. For this, we're going to call SDL_GetScancodeName, passing in the value of the ControlWidget. SDL_GetScancodeName will return the name of the key, according to its scan code. Since our ControlWidget's value is set using the scan code, we'll see the name of the key displayed. Easy! The usage of our ControlWidgets is once again done in demo.c, setting up in initDemo:
We're grabbing each of our ControlWidgets (left, right, up, down, fire, jump), and setting their coordinates and default keys. We're not setting any actions this time, but one such action could be to prevent the same key from being set to different widgets. As this is just a demo and we're not really making use of the controls, we're not too bothered by that. That's almost we need to do for our ControlWidgets. One final thing to look at is another update to input.c:
We've updated the doKeyDown function to grab the lastKeyPressed variable. When a key is pressed down, we're grabbing the event's keysym.scancode and assigning it to lastKeyPressed. This, as we've already seen, is used in widgets.c for checking which key was pressed. And that's all our widgets defined! In the next part, we'll look at how to implement our widgets into a short demo game, by bringing up an in-game menu, and changing some options. Purchase The source code for all parts of this tutorial (including assets) is available for purchase: From itch.io It is also available as part of the SDL2 tutorial bundle: |