6
\$\begingroup\$

I'm creating a game that implements a finite automaton and I don't know if I'm doing it right. Any advice?

Game class

import java.awt.Canvas;
import java.awt.event.KeyListener;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class Game extends Canvas implements Runnable, KeyListener
{
public final String TITLE = "Game";
public final int WIDTH  = 640;
public final int HEIGHT = 680;
public int LEVEL = 1;
public int FPS = 0;
public int SCORE = 0;
public int LIFE = 5;

private boolean running = false;
private Thread thread;

private BufferedImage textBackground = null;
private BufferedImage spriteSheet = null;
private BufferedImage background = null;

private int enemyCount = 3;
private int enemyKilled = 0;
private int enemyNotKilled = 0;
private boolean target = false;
private String targetText = "";

private Controller c;
private LinkedList<Enemy> e = new LinkedList<Enemy>();
private Enemy enemy;

public void init()
{
    requestFocus();
    try
    {
        spriteSheet = ImageIO.read(getClass().getResource("/spritesheet.png"));
        background = ImageIO.read(getClass().getResource("/background.png"));
    }
    catch(IOException e)
    {
        e.printStackTrace();
    }

    textBackground = getSprite(1, 1, 96, 32);
    c = new Controller(textBackground, this);
    addKeyListener(this);
    c.createEnemy(enemyCount);
    e = c.getEnemy();
}

private void start()
{
    if(running)
        return;

    running = true;
    thread = new Thread(this);
    thread.start();
}

private void stop()
{
    if(!running)
        return;

    running = false;
    try
    {
        thread.join();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

    System.exit(1);
}

public void run()
{
    init();
    long lastTime = System.nanoTime();
    final int framesPerSecond = 30;
    double ns = 1000000000 / framesPerSecond;
    double delta = 0;
    int framesCount = 0; 
    long timer = System.currentTimeMillis();
    int update = 0;  

    while(running)
    {
        long now = System.nanoTime();
        delta += (now - lastTime) / ns;
        lastTime = now;

        //if(LIFE == 0)
        //  break;   game over

        if(delta >= 1)
        {
            update();
            delta--;
            update++; 
        }
        render();
        framesCount++; 

        if(System.currentTimeMillis() - timer > 1000) 
        {
            timer += 1000;

            System.out.println("Update : " + update + " --- FPS : " + framesCount);

            FPS = framesCount;
            framesCount = 0;
            update = 0;
        }
    }
    stop();
}

private void update()
{
    c.update();

    if(enemyKilled + enemyNotKilled >= enemyCount)
    {
        targetText = "";
        LEVEL++;
        enemyKilled = 0;
        enemyNotKilled = 0;

        enemyCount += 2;
        c.createEnemy(enemyCount);
    }
}

private void render()
{
    BufferStrategy bs = getBufferStrategy();

    if(bs == null)
    {
        createBufferStrategy(3);
        return;
    }

    Graphics g = bs.getDrawGraphics();

    g.drawImage(background, 0, 0, null);

    c.render(g);

    g.dispose();
    bs.show();
}

public void keyPressed(KeyEvent ek)
{
    int key = ek.getKeyCode();
    char character = Character.toLowerCase(ek.getKeyChar());
    boolean result = isValid(key);

    if(result && !target) 
    {
        for(int i = 0; i < e.size(); i++)
            if(e.get(i).getFirstLetter() == character && e.get(i).getOnScreen())
            {
                enemy = e.get(i);
                targetText = enemy.getText();
                enemy.addIndex(1);
                target = true;
                break;
            }
    }
    else if(result && targetText.charAt(enemy.getIndex()) == character)
        enemy.addIndex(1);
}

public void keyReleased(KeyEvent ek) { }

public void keyTyped(KeyEvent ek) { }


public static void main(String[] args)
{ 
    Game game = new Game();

    game.setPreferredSize(new Dimension(game.WIDTH, game.HEIGHT));
    game.setMaximumSize(new Dimension(game.WIDTH, game.HEIGHT));
    game.setMinimumSize(new Dimension(game.WIDTH, game.HEIGHT));

    JFrame frame = new JFrame(game.TITLE);
    frame.add(game);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    //frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    game.start();
}

public boolean isValid(int key)
{
    return key >= 65 && key <= 90 ? true : false;
}
public BufferedImage getSprite(int col, int row, int width, int height)
{
    return spriteSheet.getSubimage((col * 32) - 32, (row * 32) - 32, width, height);
}
public int getEnemyCount()
{
    return enemyCount;
}
public void setEnemyCount(int b)
{
    enemyCount = b;
}
public int getEnemyKilled()
{
    return enemyKilled;
}
public void setEnemyKilled(int b)
{
    enemyKilled = b;
}
public int getEnemyNotkilled()
{
    return enemyNotKilled;
}
public void setEnemyNotkilled(int enemyNotKilled)
{
    this.enemyNotKilled = enemyNotKilled;
}
public int getScore()
{
    return SCORE;
}
public void setScore(int b)
{
    SCORE = b;
}
public int getLife()
{
    return LIFE;
}
public void setLife(int b)
{
    LIFE = b;
}
public boolean getTarget()
{
    return target;
}
public void setTarget(boolean b)
{
    target = b;
}
public String getTargetText()
{
    return targetText;
}
public void setTargetText(String b)
{
    targetText = b;
}
}

Enemy Class

import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.text.AttributedString;

public class Enemy
{
private double x ,y;
private String text;
private char firstLetter;
private AttributedString as;
private Controller c;
private BufferedImage background;
private Game game;
private int index = 0;
private int textLength = 0;
private double speed = 0.0;
private int stringWidth = 0;
private boolean onScreen = false;

public Enemy(double x, double y, double speed, BufferedImage background, Controller c, Game game, String text, int stringWidth)
{
    this.x = x;
    this.y = y;
    this.background = background;
    this.text = text;
    this.game = game;
    this.c = c;
    this.speed = speed;
    firstLetter = this.text.charAt(0);
    textLength = this.text.length();
    this.stringWidth = stringWidth;
}

public void update()
{
    y += speed;

    if(y >= 0)
        onScreen = true;

    if(index >= textLength)
    {
        game.setScore(game.getScore() + 5);
        game.setTargetText("");
        c.removeEnemy(this);
        game.setEnemyKilled(game.getEnemyKilled() + 1);
        game.setTarget(false);
    }
    if(y >= game.HEIGHT - 50)
    {
        if(game.getLife() != 0)
            game.setLife(game.getLife() - 1);

        c.removeEnemy(this);
        game.setEnemyNotkilled(game.getEnemyNotkilled() + 1);
        if(game.getTargetText().equals(text))  
        {                            
            game.setTarget(false);
            game.setTargetText("");
        }

    }
}

public void render(Graphics g)
{
    g.drawImage(background, (int)x, (int)y, null);

    as = new AttributedString(text);
    if(index >= 1)
        as.addAttribute(TextAttribute.FOREGROUND, Color.WHITE, 0, index);

    as.addAttribute(TextAttribute.FONT, new Font("Consolas", Font.BOLD, 12), 0, text.length());
    g.drawString(as.getIterator(), (int)x + getAdd(stringWidth), (int)y + 13);
}
public double getX()
{
    return x;
}
public double getY()
{
    return y;
}
public String getText()
{
    return text;
}
public char getFirstLetter()
{
    return firstLetter;
}
public void addIndex(int b)
{
    index += b;
}
public int getIndex()
{
    return index;
}
public int getTextLength()
{
    return textLength;
}
public int getAdd(int width)
{
    return (96 / 2) - (width / 2); 
}
public boolean getOnScreen()
{
    return onScreen;
}
}
\$\endgroup\$
  • \$\begingroup\$ In the future, it would help if you make life easier for reviewers and explain a bit about what your code does. Simple examples of images of input/output or similar is extremely helpful, and is likely to give your question more attention. \$\endgroup\$ – Simon Forsberg Mar 6 '14 at 11:11
7
\$\begingroup\$

There are a number of things to comment on here.

Code-Style

Java coding style puts the { opening brace at the end of the line containing the condition/construct that starts the block. Code like:

public char getFirstLetter()
{
    return firstLetter;
}

should be:

public char getFirstLetter() {
    return firstLetter;
}

Additionally, there should be an empty line after the closing } and the start of the next method declaration.

Threads

You are using swing, yet I cannot see any places where you are accommodating work that is done on the Event Dispatch Thread (EDT) vs. your game thread.

This is likely to lead to inconsistent gameplay, irregular timing, and other problems.... especially during the manipulation of the Graphics objects.

Terminology

I am not seeing a Finite Automata here... this looks pretty open-ended, not finite.

Magic Numbers

These are everywhere. There must be a better way to centralize them.

Integer Arithmetic

This line:

double ns = 1000000000 / framesPerSecond;

is doing integer arithmetic, in your case, you would expect ns to be 33333333.33..... but it will be plain 33333333.0 because the integer division is done before the value is converted to a double. Consider using:

double ns = 1000000000.0 / framesPerSecond;

which converts the one value to a double, and thus the entire expresion is evaluated in the double-precision space.

in this case, the loss of accuracy will likely not significantly affect the game, but you should be aware.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks! :) Do you have any advice on how can I implement a Finite Automaton here? \$\endgroup\$ – user37891 Mar 6 '14 at 1:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy