New Member + Snake Game

Hey there! I’m so happy to be here… with Java programmers. It wasn’t as easy as you might think - it was hard to sign up here, because of test question. Ha, ha… ;D

So… I’m daGrevis and I would love to be active member here for the next few years. Until Scala or Clojure will replace Java! ::slight_smile:

I’m not so experienced as you guys… so it would be great to get some feedback! I’m working on Snake game in 2D… it’s based on this tutorial. There are still lot of things that I don’t understand.

I need explanation! ???

Here are my source files…

Here are my problems…

  • The board’s width and height. It should be 640 * 480 and one unit should be 16 * 16. The board is 642 * 482,
  • Collisions. I tried it normal… I tried it insane, but all time it somehow don’t work. I speak about checking for walls (left, right, up, down wall). The snake goes some units (or pixels) after the wall… then the game ends (it should end immediately after crash with wall),
  • Start position of the snake. I can’t figure out how to get it in the middle (center horizontally and vertically) and it’s head in left side.

These are common problems right now. I hope that you could help! :slight_smile:

Alright, first first. Your board size problem.

private final int BOARDS_WIDTH = 640; // 16 * 40.
private final int BOARDS_HEIGHT = 480; // 16 * 30

to

protected final int BOARDS_WIDTH = 640; // 16 * 40.
protected final int BOARDS_HEIGHT = 480; // 16 * 30

This is so we can access those values from the Snake class.

add( new Board(), BorderLayout.CENTER );
pack()

to

Board b = new Board();
setSize(b.BOARDS_WIDTH, b.BOARDS_HEIGHT);
add(b);

Second, your collision problems are being caused by the lack of a real game loop.

Finally the start position of your Snake.

In your initGame method in your board class.

x[0] = 0;
y[0] = 0;

This is your start position. for the center of the board, use:

x[0] = BOARDS_WIDTH / 2;
y[0] = BOARDS_HEIGHT /2;

You have inspired me to convert the snake game I wrote using Canvas and Graphics2D objects into a tutorial.

Yay! Thanks for you reply. ::slight_smile:

I changed those fields access to ‘protected’ and changed code in Snake() method as you said, but that didn’t help. The problem still remains… if it’s measured using old-good “Print Screen” - it’s 642 x 482! Weird. This is the common problem that needs to be solved.

[quote]Second, your collision problems are being caused by the lack of a real game loop.
[/quote]
I could use threads there… Would it be okey?

[quote]This is your start position. for the center of the board, use:

x[0] = BOARDS_WIDTH / 2;
y[0] = BOARDS_HEIGHT /2;

[/quote]
This helped… a little. Right now the start position is like in middle, but there are strange bug - when initGame() is called there are drawed parts of snake in top vertically and center horizontally. They disapper after 4 * DELAY miliseconds. So… threads is the way? :persecutioncomplex:

[quote]You have inspired me to convert the snake game I wrote using Canvas and Graphics2D objects into a tutorial.
[/quote]
If I understood you correct… I can wait awesome tutorial about Snake from you? :-*

The reason for the print screen problem may be that the border of the window is being counted in the pixel count? And for a basic game like Snake, i think only the Main thread is required. A very basic example of a single threaded gameloop might be:


public void start(){
     init();//Initialize variables, load images, etc.
     while(running){
         logic();//update locations, handle input, check collision, etc.
         render();//draw stuff to the screen
     }
     cleanup();//release resources, do things required before exiting;
}

I switch to java.util.Timer… It doesn’t get any better! Board.java!

Okey… It’s so f*ckin’ weird that none can’t see my mistake! I will try to explain again.

I’m creating Snake’s clone. My app have too classes: Snake.java and Board.java.

Right now I have three problems:

  1. There are two constants - BOARDS_WIDTH, BOARDS_HEIGHT. They are meant to be dimensions for playground where snake moves and collects apples. It’s dynamic (thanks to kieve) and I can set them as I want. For example 640 x 480, 160 x 160 or whatever. Only thing I need to keep in my mind is that both numbers must divide with 16. Why? Because constant UNIT is set to 16 x 16 and, for example, when board’s dimensions are set to 160 x 160, it means that there would be 10 x 10 units large playground (160 / 16 = 10). Problem: playground is always two pixels (not units) larger than it meant to be. Like if I have set dimensions to 160 x 160, actual dimensions will be 162 x 162! Here are screenshot…

If you measure this image… after pill (apple, if you like) there are two extra pixels.

  1. Collisions. I think - it’s because problem #1. You see, there are code lines…
if( x[0] < 0 || x[0] > BOARDS_WIDTH || y[0] < 0 || y[0] > BOARDS_HEIGHT ) {

	inGame = false;

}

They should prevent snake from going into walls. Actual result is that snake goes few units in the wall - then only the game stops… i want it to stop exactly when snake collide with the wall!

  1. How to make snake start to move only when users clicks any of arrows? Right now it’s like snake starts to move her body, but her head is stopped. After joints * DELAY snake’s all joints are under her head! Ha, ha…

For the size problem, try calling (in Snake.java) setResizable(false); before you call pack();

Also, I would recommend you to use BufferedImage instead of Image. They work pretty much the same way (you paint them using the same methods), but Image is a very old class and it is outdated.

You want to change your collision detection to collision prediction.
First, is to check if a collision will occur in the next updated move, if it will, check if they change direction before then, if they don’t, end the game, if they do, let them move on their marry way.
I implemented and tested and i think this is what you want.
Add the following to the list of variables at the top of you board class (named how you see fit):

private boolean willCollide, changedDirection;

Next we need to update your checkCollision class to:

private void checkCollision() {
     changedDirection = false;
     if(left){
          if(x[0] - UNIT < 0){
               willCollide = true;
          }
     }
     if(right){
          if(x[0] + UNIT > BOARDS_WIDTH){
               willCollide = true;
          }
     }
     if(up){
          if(y[0] - UNIT < 0){
               willCollide = true;
          }
     }
     if(down){
          if(y[0] + UNIT > BOARDS_HEIGHT){
               willCollide = true;
          }
     }
}

Changed direction = false because right after we predict collision, we test if they moved within the time frame to prevent the collision.
Each if statement checks what direction you are moving, then tests if you will be out of bounds next update if you continue in that direciton.

Add the following to each of the keyAdapter if statements where you change left,right,up,down:

changedDirection = true;

Finally, in your timerTask run method, change

move();

to:

if(willCollide && !changedDirection){
     inGame = false;
}else{
     move();
}

Thanks a lot, Captain Awesome! That fixed mystic “2 pixels” problem! :slight_smile: :slight_smile:

About kieve… Thanks, thanks, thanks! But it still acts weird. ;D You see, after making you suggestions to my code… there worked as expected only left and top walls. Right and down walls didn’t worked! :-\ I find a quick solution for it…

if ( right ) {

	if ( ( x[0] + ( UNIT * 2) ) > BOARDS_WIDTH ) { // Quick fix.

		willCollide = true;

	}

}

if ( down ) {

	if ( ( y[0] + ( UNIT * 2 ) ) > BOARDS_HEIGHT ) { // Quick fix.

		willCollide = true;

	}

}

But why it acts like that?! So weird! Final code is here

OH! Right! My bad, i completely missed that. It’s like that because your x and y is measured from the top left corner of your image! What you posted is not a quick fix, it’s the correct fix =D

Great, great, great! ::slight_smile:

Last thing for today… how to fix problem #3? So that snakes and all waits until user press any arrow - then only the timer is stated. New event?

Add a boolean called started or some relevant name to your list at the top of your Board class.
Then you need to move:

timer.scheduleAtFixedRate( new ScheduleTask(), 0, DELAY )

Into each of your Adapter if statements like so:

if(!started){
     timer.scheduleAtFixedRate( new ScheduleTask(), 0, DELAY );
     started = true;
}

Yep, that did the job! Thanks again. :smiley:

I wonder… how do you know this all? I guess it’s experience and I will keep asking those silly questions at least for one year. Ha, ha. ;D ;D

I think it might be just who I am, I’m not very creative but when it comes to code, everything just flows for me. It also helps that this is a very simple program. I have only been coding for about a year and a half. Everyone can eventually see the connections in Code, some people pick this up very fast, others don’t. The best thing I can suggest is come up with an idea for a program, one you have never written before and try to write it. When you come across problems you don’t know how to solve right away, work at solving it. Use all the resources you can, and once you truly understand the problem, you will know how to fix it. Something very important though, make sure if something doesn’t work how you want it to while your writing it, fix it right away. The longer you wait, and the more Code you write after it, the less you will understand where the problem is and the harder and more time consuming it will be to fix everything.