"What you need to do is not confuse logic and rendering."

The title of this subject is a quote from ra4king, who really seems to know what he is talking about. But unfortunately I’ve broken this rule REALLY badly in my latest game. I told myself I would just finish this game no matter what and not get bogged down by what convention says I should do, but my code is turning into a great big mess. I would really some advice on how to rewrite my code without confusing logic and rendering.

I have included two screen shots. I thought this would make it a lot easier to see what I was talking about.

The number tiles are each represented as a Cell object, and they are stored in grid[][]. The user can move those arrows and then move the column and rows. This then moves each cell to its new position in the array. Each cell knows it’s row and column. When rendering I do this


       // draw cells 
        for (int row = 0; row < grid.length; row++) {
            for (int col = 0; col < grid[row].length; col++) {
                
                Cell cell = grid[row][col];
                
                if (!cell.isFree()) {
   
                    g.drawImage(cell.getImage(), getX(col),
                            getY(row), null);
                    }
            }
        }

The getX(col) method takes the column location of the array and works out it’s y coordinate.

Now things get really messy. You see the second screen shot, how the maths symbols are where the user has highlighted. Well I have a class called highlighed line that returns the cells that are highlighted, then in the render method I calculate all the positions of the maths symbols. There is a lot of calculation involved. Don’t let this code scare you away, I don’t expect you to understand or even read it all, it it’s just so you can see what I’m talking about.

 public void renderHighlightedLine(Graphics g) {
        ArrayList<Cell> cells = highlightedLine.getHighlightedCells();
      
        String line = highlightedLine.toString();
        
        int cellCount = 0;
        
        for (int i = 0; i < line.length(); i++) {
            char symbol = line.charAt(i);
            
            if (Character.isDigit(symbol)) {
                int row = cells.get(cellCount).row();
                int col = cells.get(cellCount).col();
                cellCount++;
                
                 g.drawImage(imageLoader.getImage("smallbox"),
                    getX(col), getY(row), null);
            } else if (symbol != '(' && symbol != ')') {
                renderSymbolHighlight(g, symbol, cells.get(cellCount - 1),
                        cells.get(cellCount));
            }
        }
    }
   
    /*
     * Takes the cell locations before and after the symbol to calculate 
     * it's position
     */
    public void renderSymbolHighlight(Graphics g, char symbol, Cell cell, 
            Cell cell2) {
        
        BufferedImage image = imageLoader.getImage(String.valueOf(symbol));
        int x = 0;
        int y = 0;
        int width = image.getWidth(null) - 5;
        int height = image.getHeight(null) - 5;;
        
        if (cell.row() != cell2.row()) {
           y  = Math.min(getY(cell.row()), getY(cell2.row()));
           y += CELL_SIZE;
           
        } else {
            y = getY(cell.row()) + CELL_SIZE / 2; 
        }
        
        if (cell.col() != cell2.col()) {
            x =  Math.min(getX(cell.col()), getX(cell2.col()));
            x += CELL_SIZE;
        
        } else {
            x = getX(cell.col()) + CELL_SIZE / 2;
       }
        
        x -= width / 2;
        y -= height / 2;
        
        g.drawImage(image, x, y, width, height, null);
    }

Now you see the maths problem along the top? I do a similar thing there too.

    public void renderSumProblem(Graphics2D g) {
        int x = 20;
        int y = 65;
        int cellCount = 0;

        int x2 = (stage.getWidth() - problemBorder.getWidth()) / 2; // the x2 is just tempory while i'm working something out
        problemBorder.setX(x2);
        problemBorder.setY(y - 10);
        problemBorder.render(g);
        
        ArrayList<Cell> cells = highlightedLine.getHighlightedCells();
         
        for (int i = 0; i < sumProblem.length(); i++) {
            
            char symbol = sumProblem.charAt(i);
            BufferedImage image;
            
            if (symbol == '_') {
               
                if (cellCount < cells.size()) {
                    Cell cell = cells.get(cellCount);
                    image = cell.getImage();
                   
                } else {
                    // draw question mark
                    image = imageLoader.getImage("_");
                   
                }
                
                if (i != 0 && sumProblem.charAt(0) == '_') {
                    // the previous was a box so add a space
                    x += 1;
                    
                }
                // -1 as cell size is one pixel above the size of the cell images
                g.drawImage(image, x, 
                        y + (CELL_SIZE -1  - image.getHeight(null) ) / 2, null);
               
                x += image.getWidth(null);
                
                
                cellCount++;
                
            } else {
                image = imageLoader.getImage(String.valueOf(symbol));
                x += 3;
                g.drawImage(image, x, 
                        y + (CELL_SIZE - image.getHeight(null) ) / 2, null);
                x += image.getWidth(null) + 3;
            }
          }
        
        x += 1; // add a small space after = sign
        // if there are numbers after = sign draw them
        for (; cellCount < cells.size(); cellCount++) {
            Cell cell = cells.get(cellCount);
            
            g.drawImage(cell.getImage(), x, y, null);
            x += cell.getImage().getWidth(null) + 1;
        }
    }

I could really do with some help on how to organise this. I don’t mind saying, I’m a pretty stupid person, so I won’t be offended if you have to really dumb things down for me to understand.

I don’t see what’s your problem here. As long as your game work, no bug, no time bomb and archieve the desired performance then fine.

What connect between render and logic is variable state. Logic changes it, render draws depend on state’s value.

Ok thanks. I thought people would point out how horrible that code was. But maybe it’s classed as being ok. That’s the hardest thing; knowing if I’m writing good or bad code. writing code that works is easy because if it doesn’t work I know it, but there is no one to tell me when I’m writing code that is a mess. It’s hard to find examples of game source code that’s more than two classes.

At the moment all that code was in my GamePlayScene class. I think I need to brake it up into more classes, because at the moment there is like 500 lines of code just rendering, and creating images. It’s making it hard to write more code because I am constantly loosing my place.

In general, your render depends on your logic. However, your logic should not depend on your render. Your render code should not be updating any logic. Your logic code should not be rendering anything.

The worst problem here is that Cell might have slightly confused responsibilities here, but otherwise what David Spade here said: don’t render as a side effect in your logic, and don’t read purely rendering-related state as part of your logic.

If you really want to create some extra boundaries between your logic and presentation, have Cell implement interfaces like CellContent and CellGraphic, divide up the methods between them, and don’t ever cheat by casting. Then if you get on an elegance kick, then you can factor those out to be more generic (like “Model” and “Drawable”) at some future date, and even implement them on separate classes in separate arrays. I’m not sure any of that’s necessary for your app, and you might do just fine with more fine-grained methods that avoid unnecessary statefulness, but it’s just one way you can go.

;D

If you have bad styled code, it wont disturb anyone except yourself. Harder to read/debug and gain little chance to be reusable.

On my style, I focus to logic. I’ll let logic part do everything it likes without touching Graphics/Graphics2D/SpriteBatch. Then I’ll be bad on render cause it’s only allowed to get values and use +/- for offset sake.

Thanks guys! Lots of really valuable information here.

Whats with that avatar anyway dude? I mean, why David Spade of all people?

I had no idea who he was at first. I was randomly scrolling through the default images JGO had and I chose him because he looked cool.

Now it’s stuck as me ;D

Oh and also because he’s dreamy and I want to make lots and lots of babies with him ;D

pending answer to obvious question of ra4king’s sexuality and/or gender

I’ll give you hint, he’s male. Now you have more time to think the other one :stuck_out_tongue:

Hahahaha I’m a guy and I’m straight. That last line was inspired by sproingie in IRC XD

Great! Glad we cleared that up before any babies got hurt and/or born.

The game looks interesting. If you really want a clean separation between logic and rendering, write the entire logic without any rendering at all. So the logic is only executed by your test code, and it gives back a logical representation of the board in response to any given input. Then use that API to implement your rendering.