Valhalla Legends Forums Archive | Java Programming | Recreating "Snake" - Drawing on a JFrame

AuthorMessageTime
JoeTheOdd
I'm sure pretty much everyone, even us programmers, have played the cell-phone game Snake, and pretty much know how it works. Anyhow, I'm writing it in Java in my spare time. So far I've set up a class that extends JFrame implements keyListener, and will capture the WSAD movement keys (for directing the snake), and tell you when you use a bad key. I've set up a timer to move the snake, which is just outputting a console message on firing right now. That's all just fine.

The hard part, though, is how I'm supposed to draw the snake (and hold the coordinates of his tail, but that shouldn't be nearly as hard) on an imaginary "grid" on top of the JFrame. Anyone have experience doing something like this?
March 7, 2006, 2:54 AM
iago
Well, if you need inspiration or ideas, you can check out ZSnake.  It's an open-source snake-style game written in Java. 
March 7, 2006, 4:16 AM
Myndfyr
I don't know if this is available in Java, but save yourself some headaches early on and look into double buffering to avoid flicker.
March 7, 2006, 5:48 AM
JoeTheOdd
[quote author=iago link=topic=14451.msg147787#msg147787 date=1141705009]
Well, if you need inspiration or ideas, you can check out ZSnake.  It's an open-source snake-style game written in Java. 
[/quote]

Although playing it was extremely fun (lol), it's commented in German. =(
March 7, 2006, 1:49 PM
iago
I thought the website was in French?

In any case, MyndFyre is right.  Java has a pretty extensive 2d (and 3d, if you're into that) graphics library, you could probably find out a lot about it on Java's site.  I'm sure I've seen a 2d tutorial.  But if you're going to do that, make sure you're on Java 1.5, because it's way faster than 1.4.*.
March 7, 2006, 2:15 PM
JoeTheOdd
EDIT -
Now playable and fun! All that's not coded yet is the growing of the tail, which I think will be a royal pain. Original post below:

[hr]

Hdx linked me to Graphics2D last night. I don't see the difference between that and Graphics, but oh well. I'm using Graphics.fillRect(int, int, int, int) right now. I've managed to make the dot drag across the screen and be controlled with the WSAD keys. [s]It won't go up or left (I probably did something really stupid..), and[/s] it won't clear it's path once it's gone, but I've made a ton of progress. =)

EDIT -
[code]// Key listener
import java.awt.event.*;

// Window
import javax.swing.*;
import java.awt.*;

// Timer
import java.util.Timer;
import java.util.TimerTask;

public class Snake extends JFrame implements KeyListener
{
private static final int DIR_NULL  = 0; // Direction's for the snake to move
private static final int DIR_UP    = 1;
private static final int DIR_DOWN  = 2;
private static final int DIR_LEFT  = 3;
private static final int DIR_RIGHT = 4;

private static final int SIZE = 15; // Size of the snake

private Container c; // Container, marking the JFrame background
private Timer snakeMoveTimer = new Timer(); // Timer used to fire snake moving events

private int xPos = SIZE; // X coordinate of snake
private int yPos = SIZE; // Y coordinate of snake

private int direction = DIR_NULL; // direction snake is moving

private int xPosFood = 0; // X coordinate of food
private int yPosFood = 0; // Y coordinate of food

private int score = 0; // Score!


/**
* Program entry-point
* @param args Commandline arguments
*/
public static void main(String args[])
{
new Snake();
// calls class constructor
}

/**
* Class constructor
* Creates a new instance of Snake
*/
public Snake()
{
// Set up JFrame
super("Snake!");
this.setSize(800, 500);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
c = getContentPane();
c.setBackground(Color.yellow);
this.setVisible(true);

// Add a key listener
this.addKeyListener(this);

// Create the task to move the snake with
snakeMoveTimer.schedule(new SnakeMoveTask(), 1000, 100);
}

/**
* Fired by keyListener when a key is pressed then released
* Used to tell the snake which direction to move
* WSAD directions (up down left right)
* @param e KeyEvent struct, telling us what happened in this event
*/
    public void keyTyped(KeyEvent e) {
switch (e.getKeyChar())
{
case 'w': // up
this.direction = DIR_UP;
break;
case 's': // down
this.direction = DIR_DOWN;
break;
case 'a': // left
this.direction = DIR_LEFT;
break;
case 'd': // right
this.direction = DIR_RIGHT;
break;
default:
System.out.println("Unrecognized keystroke: 0x" + Integer.toHexString((int)e.getKeyChar()));
break;
}
    }

/**
* Fired when run of SnakeMoveTask calls repaint
* Resonsible for moving the snake
*/
    public void paint(Graphics g)
    {
if((xPosFood == 0) || (yPosFood == 0))
{
// Draw food
g.setColor(Color.pink);
xPosFood = (int)(Math.random() * 800 / SIZE) * SIZE;
yPosFood = (int)(Math.random() * 500 / SIZE) * SIZE;
g.fillRect(xPosFood, yPosFood, SIZE, SIZE);
}
int old_x = xPos, old_y = yPos;
switch (direction)
{
case DIR_UP:
yPos -= SIZE;
break;
case DIR_DOWN:
yPos += SIZE;
break;
case DIR_LEFT:
xPos -= SIZE;
break;
case DIR_RIGHT:
xPos += SIZE;
break;
}
if ((xPos == xPosFood) && (yPos == yPosFood))
{
g.setColor(Color.pink);
g.clearRect(xPosFood, yPosFood, SIZE, SIZE);
xPosFood = (int)(Math.random() * 800 / SIZE) * SIZE;
yPosFood = (int)(Math.random() * 500 / SIZE) * SIZE;
g.fillRect(xPosFood, yPosFood, SIZE, SIZE);
score += 10;
}
g.setColor(Color.yellow);
g.clearRect(old_x, old_y, SIZE, SIZE);
g.fillRect(xPos, yPos, SIZE, SIZE);

// Border check
if ((xPos < 0) || (xPos > 800) || (yPos < 0) || (yPos > 500))
{
// Game over
xPos = SIZE;
yPos = SIZE;
xPosFood = 0;
yPosFood = 0;
direction = DIR_NULL;
g.clearRect(0, 0, 800, 500);
JOptionPane.showMessageDialog(null, "Game over!\n Your score: " + score + ".");
score = 0;
}
}

/**
* Fired by keyListener - stub function
* @param e KeyEvent struct, telling us what happened in this event
*/
    public void keyPressed(KeyEvent e) {
    }
/**
* Fired by keyListener - stub function
* @param e KeyEvent struct, telling us what happened in this event
*/
public void keyReleased(KeyEvent e) {
    }

class SnakeMoveTask extends TimerTask
{
/**
* Called by snakeMoveTimer when it fires
* Responsible for calling repaint, to fire paint of Snake
*/
public void run()
{
repaint();
}
}

}[/code]

EDIT -
VB moment! I forgot to break in my direction switch. Oops!
March 7, 2006, 10:05 PM
SecretShop
Consider the following:
1) A snake has a set length that increases over time
2) Each part of the snake exists until the tail surpasses it
3) The only part that moves are the head and the tail

Just after a first look, it seems like you could use a Queue that stores points on a matrix.  When the length of the Queue reaches the size of the snake just remove the tail item and push the new point of the head onto the Queue.  If you're following the motif of the cellphone game, probably just create a matrix to store the game board, possibly of integers.  0 for empty, 1 for head, 2 for body, 3 for tail.  Seems pretty simple with this design, tell me what you think.

Edit: Also I would use the directional keys only to dictate what vector the snake should move in and have it move automaticly on a timer.
March 30, 2006, 5:24 AM
JoeTheOdd
I was following the cellphone game, but this was really a "case study" of sorts to learn more about Swing and ActionListener.
March 30, 2006, 1:12 PM
Grok
Snake (by any name) is one of those classic assignments you give to people in about 3rd year CS to help them think about algorithms.  Better algorithms are needed to improve look-ahead abilities the snake has to avoid collisions.
March 30, 2006, 3:09 PM
JoeTheOdd
Yeah, this never turned out to be the old cell-phone game, snake. It was a dot moving around the screen bumping into other dots and gaining 10 points.
March 30, 2006, 3:55 PM

Search