user1150769
user1150769

Reputation: 395

Smooth animation in a snake game (java)?

I have made a basic snake game in java and I need some help with getting smoother animation. Each segment of the snake is a 20x20 square, so I made the snake move at a step of 20 pixels per tick. This makes the snake move seemingly on a grid, which means it will always line up with the food which only spawns on the "grid". Now my problem is, when I try and move it by a step of 3, it is not aligned with the "grid" anymore. Another more serious problem I have is that the snake moves by moving the first square, then settings all of the others to the next position in the array (see code). The problem is, when it moves by a step of 3, the snake will shrink so there is only a gap of 3 between each origin, instead of 20. Can anyone help with these problems? I can add the entire source code if needed.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferStrategy;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import javax.swing.Timer;

public class player1 extends Canvas implements Runnable {

private keyevents keyevent;
private element [] elements;
private element goal;
private int numSegments, score, endY, quitX, snakeSize, windowSize;
private Font scorefont;

private long cycleTime;
private final int FRAME_DELAY;
private BufferStrategy bf;
private boolean gameOver, isRunning;

public player1(Frame window) {
    setSize(new Dimension(window.getWidth(), window.getHeight()));
    FRAME_DELAY = 20;
    windowSize = 400;
    snakeSize = 20;
    score = 0;
    isRunning = false;
    numSegments = 5;
    scorefont =  new Font("Verdana", Font.BOLD, snakeSize);
    elements = new element [300];
    goal = new element(randompos(snakeSize,windowSize-snakeSize),randompos(snakeSize,windowSize-snakeSize), snakeSize, Color.green);
    keyevent = new keyevents();
    keyevent.setP1Keys(2,true);

    for (int i=0, xPos = round(windowSize/2), yPos = round(windowSize/2); i<numSegments; i++, xPos+=snakeSize) {
        elements[i] = new element(xPos,yPos, snakeSize, Color.blue);
    }

    addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent evt) {
            keyevent.moveIt(evt); 
            switch(evt.getKeyCode()){
            case KeyEvent.VK_ESCAPE:
                isRunning = false;
                screen.newMenu();
            }
        }
    });
}

public void start(player1 ex){
    isRunning = true;
    Thread gameThread = new Thread(ex);
    gameThread.setPriority(Thread.MIN_PRIORITY);
    gameThread.start(); 
}

public void run(){ 
    cycleTime = System.currentTimeMillis();
    while(isRunning){
        logic();
        render();
        sync();
    }
}

public void render() {
    if (bf==null){
        bf = getBufferStrategy();
    }
    Graphics2D g2 = null;
    try{
        g2 = (Graphics2D) bf.getDrawGraphics(); 
        g2.setColor(Color.black);
        g2.fillRect(0, 0, windowSize, windowSize);
        for (int i=0;i<numSegments;i+=1) {
            elements[i].draw(g2);
        }
        g2.setColor(Color.green);
        goal.draw(g2);
        g2.setColor(Color.orange);
        g2.fill3DRect(0, 0, windowSize, snakeSize, true);
        g2.setColor(Color.red);
        g2.setFont(scorefont);
        g2.drawString("Score: "+score, 5, snakeSize-4);
        FontMetrics fm = g2.getFontMetrics();
        g2.setColor(Color.blue);
        fm = g2.getFontMetrics();
        g2.setColor(Color.black);
        quitX = (windowSize - fm.stringWidth("ESC = quit")-5);
        g2.drawString("ESC = quit",quitX,snakeSize-snakeSize/5);
    }finally{
        g2.dispose();
    }
    bf.show();
    Toolkit.getDefaultToolkit().sync();
}

public void sync(){
    cycleTime = cycleTime + FRAME_DELAY;
    long difference = cycleTime - System.currentTimeMillis();
    try {
        Thread.sleep(Math.max(0, difference));
    }
    catch(InterruptedException e) {
        e.printStackTrace();
    }
}

public void logic(){
    int x = elements[0].getX();
    int y = elements[0].getY();
    Rectangle elementrect = elements[0].getRect();  
    keyevent.setP1Pressed(false);

    //Movement
    for(int i = numSegments-1; i>0; i--){
        elements[i].setX(elements[i-1].getX());
        elements[i].setY(elements[i-1].getY());
    }   
    if(keyevent.getP1Keys(0)){
        elements[0].setY(y+20);
    }
    else if(keyevent.getP1Keys(1)){
        elements[0].setY(y-20);
    }
    else if(keyevent.getP1Keys(2)){
        elements[0].setX(x-20);
    }
    else if(keyevent.getP1Keys(3)){ 
        elements[0].setX(x+20);
    }
    if(elements[0].getX()>windowSize-snakeSize){
        elements[0].setX(0);
    }
    else if(elements[0].getX()<0){
        elements[0].setX(windowSize-snakeSize);
    }
    if(elements[0].getY()>windowSize-snakeSize){
        elements[0].setY(0);
    }
    else if(elements[0].getY()<0+snakeSize){
        elements[0].setY(windowSize-snakeSize);
    }
    for(int i=0; i<numSegments; i++){
        elements[i].updateRect();
    }       

    //Collision
    if(elementrect.intersects(goal.getRect())){
        numSegments+=1;
        elements[numSegments-1] = new element(elements[numSegments-2].getX(),elements[numSegments-2].getY(), snakeSize,Color.blue);
        goal.setX(randompos(snakeSize,windowSize-snakeSize));
        goal.setY(randompos(snakeSize,windowSize-snakeSize));
        score+=1;
    }
    for(int i=1; i<numSegments; i++){
        if(elementrect.intersects(elements[i].getRect())){
            gameOver();
        }
    }
}

public void gameOver(){
    isRunning = false;
}

public int randompos(int min,int max){
    System.out.println(snakeSize);
    int range = max-min;
    Random randompos = new Random();
    int pos = randompos.nextInt(range)+min;
    pos*=2;
    pos += snakeSize;
    pos /= snakeSize*2;
    pos *= snakeSize*2;
    pos/=2;
    return pos;
}
}

Upvotes: 4

Views: 2614

Answers (2)

trashgod
trashgod

Reputation: 205885

In addition to @mKorbel's excellent advice, consider using a java.util.Queue for your snake segments:

Queue<Element> snake = new LinkedList<Element>();

Then a single animation step requires simply removing the oldest Element and adding a new one that's offset according to your games's rules for position and velocity:

snake.remove();
snake.add(newElement());
this.repaint();

Upvotes: 1

mKorbel
mKorbel

Reputation: 109823

1) use Java naming conventions correctly player1 should be Player1 etc

2) don't use pre_historic AWT Canvas, use Swing JPanel, JLabel or JComponent, same for Frame ---> JFrame

3) don't use KeyListener, because required Focus for your Canvas, easiest is use KeyBinding

4) you have issues with Concurency in Swing, use javax.swing.Timer rather than wrong implemntations for Runnable#Tread

5) never use Thread.sleep(Math.max(0, difference)); for AWT/Swing J/Components, because block EventDispatchTread, and by wrong implentations for Thread.sleep() yoour GUI will be freeze os stay un_responsible, use javax.swing.Timer for dealying any event in the Swing

6) post here a SSCCE that demonstrating your real uissues, instead of bunch of un_relatted code and with somw anothere missed (maybe yes, maybe not) important classes,

Upvotes: 6

Related Questions