Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (739)
Games in Android Showcase (224)
games submitted by our members
Games in WIP (820)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Is it just me, or does everyone's GUI code look like spaghetti?  (Read 4873 times)
0 Members and 1 Guest are viewing this topic.
Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Posted 2014-05-14 07:01:19 »

As I write my GUI for my latest game, probably one of the most complicated GUIs I've had to write because it's 100% mouse driven and has a bazillion buttons to press on the interface, I find my code becoming spaghetti. I was curious how you guys handle your GUI code? Mine seems to be turning into a stupid amount of booleans and if/else statement, for example, here's my current state of my left mouse button:

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  
if(input.isMousePressed(Input.MOUSE_LEFT_BUTTON)){
   //Toggle Menu tabs in and out
   if ((mouseX > rightHandAlignX+4) && (mouseX < rightHandAlignX+12) && (mouseY > rightHandAlignY+144) && (mouseY < rightHandAlignY+182)){
      rightHandToggle();
      return;
   }
   if ((mouseX > leftHandAlignX+44) && (mouseX < leftHandAlignX+52) && (mouseY > leftHandAlignY+144) && (mouseY < leftHandAlignY+182)){
      leftHandToggle();
      return;
   }
   //Press buttons on right hand
   for (int x = 0; x < 2; x++) {
      for (int y = 0; y < 8; y++) {
         if ((mouseX > rightHandAlignX+16+(x*36)) && (mouseX < rightHandAlignX+49+(x*36)) && (mouseY > rightHandAlignY+31+(y*20)) && (mouseY < rightHandAlignY+48+(y*20))){
            System.out.println("You pressed button: "+((x*8)+y));
            //Light placement button
            if ((x*8)+y == 0){
               if (modePlaceLight){
                  modePlaceLight = false;
                  return;
               }else{
                  modePlaceLight = true;
                  return;
               }
            }
            //Place blood
            if ((x*8)+y == 1){
               if (modeBlood){
                  modeBlood = false;
                  return;
               }else{
                  modeBlood = true;
                  return;
               }
            }
            if ((x*8)+y == 2){
               changeTime("day");
            }
            if ((x*8)+y == 3){
               changeTime("night");
            }
         }
      }
   }

   if (modePlaceLight){placeLight(getTileX(), getTileY()); return;}  
   if (modeBlood){addBlood() return;}        
   selectEntity(getMouseOnMapX(), getMouseOnMapY());


Basically it's seeing where the mouse is, the forloop just assembles a set of 16 "box areas" in an 2x8 pattern, when you click on it, it checks what box you pressed and enables whatever is under that button. Then, below, it just has a ton of boolean and if/else checks (only 2 for now, but that will quickly expand to about 20 when I have to check for all the other situations) then it goes to mini-methods that tell other classes what to do.

So, do you guys run into this problem with complex GUIs as well? It really feels like a huge tangled mess, because almost everything in the game will be tied to the right and left click mouse buttons, meaning it's probably going to be a pretty long little section of code that sort of sprawls out in the right direction based on a ton of if/else and booleans.

Really, the GUI itself works great and even though it *looks* messy it's not actually hard to code, it just feels wrong. So I was curious what other's experiences have been coding their own custom GUIs.

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Offline hwinwuzhere
« Reply #1 - Posted 2014-05-14 07:13:40 »

Yes. I recognize this problem. Very Well. Smiley

I wrote a game a couple of weeks ago with a messy GUI that I implemented afterwards. It became such a mess I decided to create a model for GUI's to use for every game after that. But since I decided to rewrite the game I made with this new model, I ran into several problems. Using workarounds or complete code changes I was able to 'fix' these problems, but it cost me the cleanness-ish of the code (partially).

I think the main problem is that you start of coding a game, and then adding the GUI (menu's etc.) elements. If you want everything to be compatible, you need to start by writing the framework for the GUI and it's coöperation with the game itself.

So yes, for me: I recognize this problem. You're not alone Grin

There are two kinds of people in this world: Those who can extrapolate from incomplete data,
Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Reply #2 - Posted 2014-05-14 07:22:34 »

Yes. I recognize this problem. Very Well. Smiley

I wrote a game a couple of weeks ago with a messy GUI that I implemented afterwards. It became such a mess I decided to create a model for GUI's to use for every game after that. But since I decided to rewrite the game I made with this new model, I ran into several problems. Using workarounds or complete code changes I was able to 'fix' these problems, but it cost me the cleanness-ish of the code (partially).

I think the main problem is that you start of coding a game, and then adding the GUI (menu's etc.) elements. If you want everything to be compatible, you need to start by writing the framework for the GUI and it's coöperation with the game itself.

So yes, for me: I recognize this problem. You're not alone Grin

Fun parts going to see what my left mouse button code looks like when I add all the buttons currently planned, right now on screen is 29 buttons (and more on the way) that all need to react to the left mouse button in some way or another. Some open menus, some are toggles to place buildings, some are just GUI interface things like changing pages. All of them do their own thing, and really don't have much to do with each other, but they're all going to end up inside the left mouse button code one way or another, even if I give each button it's own method the left mouse button will still end up looking something like this:

rightCategoryArrow();
leftCategoryArrow();
rightHandTab();
leftHandTab();
<<the forloop in the other post that assembles the button layout on the right tab>>
mainMenuButton();
villagerButton();
heroButton();
barterButton();

...and so on and so forth.. then shortly followed by about 2 dozen of these

if (someButtonOn){methodToRunOnClick(); return;}

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ctomni231

JGO Wizard


Medals: 99
Projects: 1
Exp: 7 years


Not a glitch. Just have a lil' pixelexia...


« Reply #3 - Posted 2014-05-14 07:23:31 »

Well the huge thing about keeping input code neat is completely separating it from the logic code. You can prevent spaghetti by having a boolean track your button presses and pushing all that code to the update method instead. That way, no nested loops need to be in the input method. Just my 2¢.

Offline hwinwuzhere
« Reply #4 - Posted 2014-05-14 07:25:36 »

Well the huge thing about keeping input code neat is completely separating it from the logic code. You can prevent spaghetti by having a boolean track your button presses and pushing all that code to the update method instead. That way, no nested loops need to be in the input method. Just my 2¢.

Yes! That is what I tried to do consistently. However everyones code is difference, so everyone will encounter different problems with different solutions that don't always work by putting it into the update method (or something like that).

There are two kinds of people in this world: Those who can extrapolate from incomplete data,
Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Reply #5 - Posted 2014-05-14 07:26:51 »

Well the huge thing about keeping input code neat is completely separating it from the logic code. You can prevent spaghetti by having a boolean track your button presses and pushing all that code to the update method instead. That way, no nested loops need to be in the input method. Just my 2¢.

oh, that's actually what I'm doing. The nested forloop doesn't actually do anything functional related to the execution at all, it's just shrinking the code down for detecting where the mouse is for the 16 right side buttons that are all lined up, then the IF statement is just seeing what button you're over in the loop and executing the method attached to that button.

In this screenshot, on the right hand tab thats open, all that forloop is doing is driving the "if mouse is between X and Y do this" for all those solid black boxes (to be buttons someday), that way I dont have to rewrite the exact same code for all 16 buttons.


- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Offline Nate

« JGO Bitwise Duke »


Medals: 167
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #6 - Posted 2014-05-14 07:38:45 »

I've put a lot of effort into making UI code nice: DSL, visual editor, etc. The best thing I've come up with is: use a sane 2D scene graph that has the features needed for UIs (drawing, layout, events), use a flexible layout manager with a builder style API, and organize your code by separating it into 3 parts: creation, layout, and events. I can suggest scene2d.ui and TableLayout for the first two. You can see this example UI code for how it looks in practice. This code isn't too bad, but could be organized slightly better. For complex UI code I usually have 3 separate methods for creation, layout, and events. It really makes a difference, as it is hard to make any sense of layout code when it is mixed with other things.

Offline ctomni231

JGO Wizard


Medals: 99
Projects: 1
Exp: 7 years


Not a glitch. Just have a lil' pixelexia...


« Reply #7 - Posted 2014-05-14 07:41:33 »

Maybe an example of what I mean would help...

http://www.java4k.com/index.php?action=games&method=view&gid=406#source

If you scroll all the way to the bottom, you'll see that boundaries are checked in the logic, where the spaghetti code lives. The mouse input code is kept nice and tidy. It is a style choice, yes, but it keeps all clutter in one spot which may or may not be what you are going for.

 There might not be any other solution for this, but realistically it makes no difference because the work has to be done somewhere.  Undecided

Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Reply #8 - Posted 2014-05-14 07:48:38 »

Maybe an example of what I mean would help...

http://www.java4k.com/index.php?action=games&method=view&gid=406#source

If you scroll all the way to the bottom, you'll see that boundaries are checked in the logic, where the spaghetti code lives. The mouse input code is kept nice and tidy. It is a style choice, yes, but it keeps all clutter in one spot which may or may not be what you are going for.

 There might not be any other solution for this, but realistically it makes no difference because the work has to be done somewhere.  Undecided

Ah, I see what you mean. I thought about doing something similar, and attaching the mouse to a method I'd keep somewhere else that handles all the GUI button clicking. At the very least it (visually) gets the code out of my controls/input area so it's easier to read everything else there.

But like you said, the spaghetti eventually has to go somewhere anyway. It seems like a large majority of GUI code ends up like this, because you end up with a lot of similarly functioning buttons all under the same controls that are all just-different-enough you really can't combine much of anything together.. so for code as simple as detecting what button you're hovering over and what method to run when you click on it can easily skyrocket to a hundred+ lines of code just for a dozen buttons.

The few examples I've found where people claimed to have nice GUI code were using other GUI libraries to assist them that, surprise surprise, are just spaghetti code in hiding because all the spaghetti is just hiding away in the library, and if they brought it out they'd have a similar mess anyway.

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Offline Nate

« JGO Bitwise Duke »


Medals: 167
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #9 - Posted 2014-05-14 07:52:21 »

Look at how a 2D scene graph works.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Reply #10 - Posted 2014-05-14 07:59:24 »

Look at how a 2D scene graph works.

Interesting, that's very similar to what I wanted my final product to be, although I'm not using LibGDX. But at least it shows me that the "big picture" I wanted to eventually reach is possibly in the right direction. Cheesy

My GUI's actual functionality is in it's infant stage right now, mainly because I've spent the last 2 days mulling over how I wanted to do it while I worked on other things, and not doing much actual coding on it. I might try to mimic how Scene2d works, see if I can pull something together.

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Offline pjt33

« JGO Spiffy Duke »


Medals: 40
Projects: 4
Exp: 7 years



« Reply #11 - Posted 2014-05-14 08:41:42 »

It seems like a large majority of GUI code ends up like this, because you end up with a lot of similarly functioning buttons all under the same controls that are all just-different-enough you really can't combine much of anything together.. so for code as simple as detecting what button you're hovering over and what method to run when you click on it can easily skyrocket to a hundred+ lines of code just for a dozen buttons.
It shouldn't. The code in the methods might run to that, but the code to dispatch them should be much simpler. Unless you have so many buttons that you need a BSP or quad-tree, you just need to register tuples of (x, y, width, height, strategy) and loop through them.
Offline hwinwuzhere
« Reply #12 - Posted 2014-05-14 09:47:04 »

@pjt33 I agree, it shouldn't

I just check every update where my mouse hovers over.

e.g.:

The code for your menu (GUI) would look like this:

1  
2  
3  
4  
5  
public void update() {
for (Button b : buttons) {
b.hover(mouseX, mouseY);
}
}


and then the code for the button:

1  
2  
3  
4  
5  
6  
public void hover(int x, int y) {
if (x >= this.x && x < this.x + width && y >= this.y && y < this.y + height) {
// Do the hover stuff and things.
} else {
// Just do stuff you want to do when not doing the hover stuff and things.
}


Smiley

There are two kinds of people in this world: Those who can extrapolate from incomplete data,
Offline Opiop
« Reply #13 - Posted 2014-05-14 11:36:19 »

I separate my buttons out into classes which extend from a superclass called Button. Button (the superclass) has a couple of methods, and is abstract:
update()
render() //not entirely relevant, shown for the sake of completeness
checkClicked()
abstract onClick ()

In the update method I check if checkClicked returns true, and if it does I fire off the onClick method, which every child implements. Obviously I then create child classes and fill in the onClick method. Then my GUI code is much simpler, all I have to do is create a list of buttons, position then, render them and then update them. I don't need huge long if statement blocks and I become a happy person because GUIs suck Smiley

Maybe that helps?
Offline saucymeatman
« Reply #14 - Posted 2014-05-14 22:22:54 »

I separate my buttons out into classes which extend from a superclass called Button. Button (the superclass) has a couple of methods, and is abstract:
update()
render() //not entirely relevant, shown for the sake of completeness
checkClicked()
abstract onClick ()

This worked pretty well for RainbowHippie, it was clean enough. One problem I ran into is that it quickly became necessary for my GUI elements to check eachother's states. For example, we dont want button A working if screen B is active. Looking back, really the answer to that would have been implementing a finite stat machine. Here is a great look into how they can help make your life alot easier in certain circumstances : http://gameprogrammingpatterns.com/state.html
Offline gene9

Senior Devvie


Medals: 10



« Reply #15 - Posted 2014-05-15 16:25:17 »

- Break large functions/procs into smaller functions/procs
- Code is usually easier to write than it is to read. Especially large code bases.
- If you have lots of functionality, you will have lots of code.
- Study functional programming in languages like Scala/Haskell, if you want some more advanced ideas on code elegance and organization.


Offline CodeHead

JGO Knight


Medals: 52


From rags to riches...to rags.


« Reply #16 - Posted 2014-05-15 17:31:51 »

One problem I ran into is that it quickly became necessary for my GUI elements to check eachother's states. For example, we dont want button A working if screen B is active. Looking back, really the answer to that would have been implementing a finite stat machine.

Hmm, I handle this scenario by placing a clear "panel" over the controls I need disabled, then drawing the modal window/controls inside of that panel. It prevents user input events from reaching the disabled controls (modality) and makes swapping entire layers of the GUI in/out a breeze. Adding FSMs in this scenario would seem to be adding unnecessary complexity to a straightforward problem, at least on first consideration.

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Reply #17 - Posted 2014-05-15 18:02:35 »

Anything GUI related scares the shit out of me tbh, soon as I start coding it, just becomes a tangled mess.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline tyeeeee1
« Reply #18 - Posted 2014-05-15 18:49:27 »

For checking if the mouse is hovering over something. Why not just use Rectangles and detect intersection? That's what I do to make my custom buttons work.
Offline Herjan
« Reply #19 - Posted 2014-05-15 19:32:18 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
            if ((x*8)+y == 0){
               if (modePlaceLight){
                  modePlaceLight = false;
                  return;
               }else{
                  modePlaceLight = true;
                  return;
               }
            }
            //Place blood
            if ((x*8)+y == 1){
               if (modeBlood){
                  modeBlood = false;
                  return;
               }else{
                  modeBlood = true;
                  return;
               }
            }


Sorry, but I considered this 'code' funny enough to quote  Kiss
1  
modeBlood = !modeBlood;


To make this post a little bit useful, I also used the Rectangle class and searched for an intersection.
1  
button.intersect(Point mouse); //returns boolean
Offline Rayvolution

« JGO Spiffy Duke »


Medals: 379
Projects: 2
Exp: 2 years


Resident Crazyman


« Reply #20 - Posted 2014-05-15 21:56:10 »

Sorry, but I considered this 'code' funny enough to quote  Kiss
1  
modeBlood = !modeBlood;


Originally the code did more than swap a boolean, I just never went back to clean it up. Wink

Yeah, I might try out the rectangles and intersects, that sounds like a pretty good idea because you only have to specify coordinates in one single place, then just see if the mouse is intersecting, if it is, do whatever is under the button.

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Now on Steam!
LIVE-STREAMING DEVELOPMENT: http://www.hitbox.tv/rayvolution
Offline loom_weaver

JGO Coder


Medals: 17



« Reply #21 - Posted 2014-05-16 00:22:27 »

I've written a "simple" (1) panel-based UI system for OpenGL that operates somewhat like Swing except its painted 60 fps.

It has mouse (move, drag, press, release, enter, exit, click, etc.) and key events (press, release) that are dispatched to the panels which in turn dispatches events to any child components.

I've really come to appreciate the amount of effort required to write any UI library.  I've probably spent close to 30 working days in total on it and each time I revisit it to add functionality I find new bugs and new ways to do things.

Recently I've added some more complicated UI controls such as auto-paginating text areas, single-selection lists, and drop-down selection lists.

Overall the code is clean and manageable but wowsers, it took considerable effort.

(1) "Simple" in functionality but not simple in code.
Offline Riven
Administrator

« JGO Overlord »


Medals: 1313
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #22 - Posted 2014-05-16 14:35:43 »

...

Sorry, but I considered this 'code' funny enough to quote  Kiss
1  
modeBlood = !modeBlood;


To reduce the redundancy and verbosity:
modeBlood ^= true;
(it might look obscure, but your brain will soon recognize this as 'toggle')



On topic:
Just add support for panels, in which components have an offset relative to their parent. Then pump events through the root, and let the panel deliver the events to the descendants, making the coordinates relative to each component you pass them through - just like AWT, Swing, HTML, etc.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Offline hwinwuzhere
« Reply #23 - Posted 2014-05-16 14:37:05 »

To reduce the redundancy and verbosity:
modeBlood ^= true;


true. Just learned this @ school Smiley Makes life so much easier.

There are two kinds of people in this world: Those who can extrapolate from incomplete data,
Offline Nate

« JGO Bitwise Duke »


Medals: 167
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #24 - Posted 2014-05-16 14:40:03 »

Just add support for panels, in which components have an offset relative to their parent. Then pump events through the root, and let the panel deliver the events to the descendants, making the coordinates relative to each component you pass them through - just like AWT, Swing, HTML, etc.
Aka a scene graph! Cheesy I don't think they are listening though.

Offline Riven
Administrator

« JGO Overlord »


Medals: 1313
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #25 - Posted 2014-05-16 14:40:49 »

Yeah, I explicitly described a scene-graph, because I felt it would otherwise be ignored Pointing

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 
Ecumene (61 views)
2017-09-30 02:57:34

theagentd (82 views)
2017-09-26 18:23:31

cybrmynd (189 views)
2017-08-02 12:28:51

cybrmynd (189 views)
2017-08-02 12:19:43

cybrmynd (194 views)
2017-08-02 12:18:09

Sralse (206 views)
2017-07-25 17:13:48

Archive (770 views)
2017-04-27 17:45:51

buddyBro (904 views)
2017-04-05 03:38:00

CopyableCougar4 (1454 views)
2017-03-24 15:39:42

theagentd (1333 views)
2017-03-24 15:32:08
List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05

SF/X Libraries
by SkyAphid
2017-03-02 06:38:56

SF/X Libraries
by SkyAphid
2017-03-02 06:38:32

SF/X Libraries
by SkyAphid
2017-03-02 06:38:05

SF/X Libraries
by SkyAphid
2017-03-02 06:37:51
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!