janbodnar/Java-Sokoban-Game

i created an advanced variant, based on yours

Drachenbauer opened this issue · 14 comments

Hello

I downloaded your sokoban-code and made some upgrades:

-I made a more detailed player-character with animated movement from tile to tile (also looks in movement direction)

-I added the ability to keep up to 10 levels to switch between.

-I added some status-info-texts above the level (level-number, total baggage-number and number of solved ones and a step-counter)

-I made a fixed window-size for levels with a maximun size of 32 tiles width and 20 tiles hight (smaller levels are alligned in the top left corner of the window, with the offset used).

-I added a set of different wall-styles to switch between with some letter-keys.

and a tip:
in the buildWorld method, there is a for-loop.

But i found out, that there is a more clean looking way to create this for-loop:

for (Actor item : world)
{
g.drawImage(item.getImage(), item.x(), item.y(), this);
}

use png-graphics of the same size (pixels) for all game-tiles.
for a smaller object, just put it centrated into a transparent graphic of your given size.
so you can use this one simple line of code for all game-tiles.

And put the drawString-call (with the "if", that holds it) out of the loop, below it.

if (isCompleted) {
g.setColor(new Color(0, 0, 0));
g.drawString("Completed", 25, 20);
}

it´s ok to check this only once per repaint-call (otherwhise it is checked once per tile of the level, but that´s not needed)

Hi, can you upload the code in your repository?

i don´t have a repository for this game.

i just made the modifications in eclipse.

But maybe i´ll create one for it.

So, here it is:
https://github.com/Drachenbauer/AngryBirdsSokoban

i created graphics in Angry Birds theme.
So i control one of theese birds and have to push eggs back into nests.
Actually i created little sprites of Stella, the pink bird.

so you can look, what i changed (i´m still thinking about more levels, and i stil have to erplace the levels 1 and 2).

I noticed, that all key-controls in your version are disabled, if the Level is solved.
So you only can close the window then.
I found the reason why this happens, and changed it, that only the arrow keys are disabled after solving a level.
so it´s always possible to restart the level or choose another one and you don´t need to close the window.

I have checked your enhancements; it's nice!

now i worked some more at it:

Now it has a ondo one step- function with the backspace-key

and i made the eggs switching between two graphics.
if exactly in a nest, they get a graphic, that make them look like really in the nest.
in other positions, they have a shadow like the bird.

also i found some more cleanup in the code.

the board-class has got many for-loops, wich work through an array-list of class-instances.

i turned them all into this style:

for (Class instance : array_list)
{
things to do here
}

the names in the round braces are just a sample here:
"array_list" is for the name of that special ArrayList, you want to work through in this for-loop
"Class" is for the name of a class-file, that has instances in the ArrayList.
and "instance" is for the name of an instance of the class (you can choose it free here, but than you have to use it for some method-calls inside the curly braces).

This does exactly the same like yours, but looks cleaner and does not need the first line inside the curly braces any more (just like i sayd for in the buildWorld method).

and i saw, that you placed exactlo the same movement-method into the player-and box-classes.
so i created a Movable-class, that extends the Actor-class and put all the movement-stuff there (it´s now abit more, because of the undo-function).
than i extended my two moving game-objects (the bird and the egg) from the class Movable.
So the moving methods and variables are now located in only one file.

And to give it a bit structure and cleanup, i added a folder ("package" in eclipse) for all the game-tiles-classes.
so they are not more mixed up with the main classes of the game in the project-path.

and i fixed a little bug with the animation-movement:
i noticed, that the bird rushed through the walls, as i pressed an arrow-key a bit longer.
now it keeps moving with it´s animation-speed and stops at walls, if i keep pressing an arrow key.
i just had to disable key-controls as long as one animation goes.

I think, it´s a good idea for you to use some of my changes in the game for your tutorial-one-too:

-the shorter for-loops for working through an ArrayList.

-this:
if (isCompleted) {
return;
}
goes inside the four arrow-key-cases of the key-controlling to be able to restart the level after finishing it.

-my organisation of the game-tile-classes, where a movable-class extents from the main-tile-class to hold all variables and methods, wich only player and box (baggage)-class both use (they extend from the movable class) and put all game-tile-classes in an extra folder (in eclipse a package) to separate them abit from the main-classes ("Sokoban" and "Board").

-your level has two tiles thick vertical walls at the left side.
I think, it looks better, if you reduce this wall-thickness to only one tile.
It´s inside stays exactly the same.

Link to my repository again:
https://github.com/Drachenbauer/AngryBirdsSokoban

Now i upgrafed the undo function, that it can undo all moves, made so far in the level.

but i had to separate the egg/box movement from the collision-check.

now it is an extra method, called after the collision-checks in the arrow-key-controls.

so it´s only called, if the player can move and it checks the collision with the player to fint out, if this egg/box has to move now

this is needed to make it possible to record player- and egg/box-positions each time, the player can move.

Now i added a custom window icon (instead of the basic java-logo) to the game-window.
it also appears in the taskbar.

A game looks more professional, if it has got a window icon, that fit´s to it´s content.

Now i added a custom window icon (instead of the basic java-logo) to the game-window.
it also appears in the taskbar.

A game looks more professional, if it has got a window icon, that fit´s to it´s content.

sorry for closing and reopening, but i saw, that my last comment (the one about the window-icon) was not added, it´s text was still in the comment editer.
But the green comment-button was disabled and i didn´t know how to send it now...

Now i changed the way to choose levels fron the number-keys to a dialog with a drop-down-listbox (combobox).

so i can add as many levels as i want.
i just have to add the level-strings to the new Array-List in the Levels-class.
all other code about the levels will automaticly use the new list-length.

Now i added a seccond dialog:

So the wall-designs can be changed the same way, too

Now it has three dialogs:

one to choose the level, one to choose a bird from the Angry-Birds-flock as the player-character and one to choose a wall-design.

It also saves the actual level, and wich ones are solved in an ini-file.
And the choosen bird and wall too.

I found this in the collision-test of the sokoban (in yours it has "bag" instead of "box"):

`if (!box1.equals(box2))
     {
     }

But i think, this is not needed to make the game-mechanic work.
if the for-loop uses the same instance from the list in both names, it always will find the same x- and y-values in both.
So "box1.isLeftCollision(box2)" or other directions will never be true in this cases and the whole collision-test will still give the same result to the same constellations of the boxes, like yours, but looks shorter and simpler.

And i also think, it will look better, if you call your instances there "bag1" and "bag2" instead of "bag"
and "item".
It´s because both are instances of the bags (in my versions boxes or eggs).

This is now one of the cases of my collision-check:

case LEFT_COLLISION:
    
    for (Egg egg1 : eggs)
    {
        if (bird.isLeftCollision(egg1))
        {
            if (checkWallCollision(egg1, LEFT_COLLISION))
            {
                return true;
            }
            
            for (Egg egg2 : eggs)
            {
                if (egg1.isLeftCollision(egg2))
                {
                    return true;
                }
            }
        }
    }
    
    return false;

I also changed the order of the parts inside the case a bit, because it doesn´t need to check the wall-collision in each round of the inner for-loop.
Now it checks this at first and then starts the inner for-loop, if the first check is not true.

Now i turned the method, that you named "checkBagCollision" into an universal-collision-checker.
I did this by adding a wall-collision-check for the player at the top of each case.

now the method is just named "checkCollision" and is now the only one, that has to be called in the arrow-key-controls to check, if the player can move into this direction.

And now i got rid of the whoole wallColision-method.
i used it´s content in the universal checkCollision-method.
it looks very logic, becaue the parts of a case thee look very similar.

            case LEFT_COLLISION:
            
            for (Wall wall : walls)
            {
                if (player.isLeftCollision(wall))
                {    
                    return true;
                }
            }
            
            for (Box box1 : boxes)
            {
                if (player.isLeftCollision(box1))
                {
                    for (Wall wall : walls)
                    {
                        if (box1.isLeftCollision(wall))
                        {    
                            return true;
                        }
                    }
                    
                    for (Box box2 : boxes)
                    {
                        if (box1.isLeftCollision(box2))
                        {
                            return true;
                        }
                    }
                }
            }
            
            return false;