--WORKING ON A SHORTER VERSION OF THE QUESTION--
So, this is someting I can't really find that much information on online.
I am making a game together with my friend and we came accross a problem when we wanted to move some objects in a thread. We have one object, GroundBlocks, which is working fine and how it is supposed to.
Then we have the other object, Obstacles, that uses the same "method" of printing and processing but doesn't show up.
I did some troubleshooting myself and I noticed that the paintComponent block in Obstacles isn't beeing called by the .setLocation we added in the thread.
As I mentioned, I didn't find a whole lot online when searching myself but through testing, I know the numbers are correct but it is just not showing.
Any ideas on what the issue might be with the .setLocation not working?
--Relevant code from the thread class--
// Creating objects
public static GroundBlocks[] ground = {new GroundBlocks(), new GroundBlocks(), new GroundBlocks()};
public static Obstacles[] obstacle = {new Obstacles(), new Obstacles(), new Obstacles(), new Obstacles(), new Obstacles()};
// Creating block dimensions
final int GROUND_HEIGHT = resY - GroundBlocks.getGroundHeight();
final int GROUND_WIDTH = ground[1].getGroundWidth();
int[] groundX = {0, GROUND_WIDTH, GROUND_WIDTH*2};
final int OBSTACLE_HEIGHT = obstacle[0].getHeight();
final int OBSTACLE_WIDTH = obstacle[0].getWidth();
int[] obstacleX = {newCoord(0), newCoord(0), newCoord(0), newCoord(0), newCoord(0)};
int[] obstacleY = {newCoord(1), newCoord(1), newCoord(1), newCoord(1), newCoord(1)};
// Setting game animation movement
final int MOVEMENT_SPEED = 7;
// Setting timer object with preferred FPS
Timer time = new Timer(Init.getFps(0), this);
public void run() {
System.out.println("A new MainThread has been initiated");
time.start();
// Setting ground block size, starting location and image
for(int i = 0; i < ground.length; i++) {
ground[i].setLocation(groundX[i], GROUND_HEIGHT);
ground[i].setSize(GROUND_WIDTH, GROUND_HEIGHT);
ground[i].setGroundImage(pickRandomGroundImage());
}
// Setting background image for obstacles
for (int i = 0; i < obstacle.length; i++) {
obstacle[i].setSize(OBSTACLE_WIDTH, OBSTACLE_HEIGHT);
obstacle[i].setLocation(obstacleX[i], obstacleY[i]);
obstacle[i].setObstacleImage(img5);
// System.out.println(obstacle[i].getLocation());
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
for(int i = 0; i < ground.length; i++) {
// Respawning ground block
if(groundX[i] <= -resX) {
groundX[i] = GROUND_WIDTH*2 - MOVEMENT_SPEED;
ground[i].setGroundImage(pickRandomGroundImage());
}
// Animating ground block
ground[i].setLocation(groundX[i] -= MOVEMENT_SPEED, GROUND_HEIGHT);
}
for (int i = 0; i < obstacle.length; i++) {
// Resetting obstacle block
if ((obstacleX[i] + OBSTACLE_WIDTH) <= -10) {
obstacleX[i] = newCoord(0);
obstacleY[i] = newCoord(1);
obstacle[i].setObstacleImage(img5);
}
// Animating obstacle block
obstacle[i].setLocation(obstacleX[i] -= MOVEMENT_SPEED, obstacleY[i]);
if (i == 0) {
System.out.println(obstacle[i].getLocation());
}
}
}
--GroundBlocks class--
public class GroundBlocks extends JPanel{
// Declarations
static int resX = Main._init.getResX();
static int resY = Main._init.getResY();
static String font = Main._init.getOurFont();
private static int groundWidth;
private static int groundHeight;
private Image chosenImage;
public GroundBlocks() {
System.out.println("GroundBlock created");
setScaleIndex();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Draw image background
g.drawImage(chosenImage, 0, 0, this);
}
// Applying the scaleIndex to the ground objects X and Y dimensions.
// Size changes can be made manually in Init.
public void setScaleIndex() {
groundWidth = (int) (Init.getGroundSize(0) * Init.getScaleIndex());
groundHeight = (int) (Init.getGroundSize(1) * Init.getScaleIndex());
}
public int getGroundWidth() {
return groundWidth;
}
public static int getGroundHeight() {
return groundHeight;
}
public void setGroundImage(Image image) {
chosenImage = image;
}
}
--Obstacles code--
public class Obstacles extends JPanel{
// Declarations
static int resX = Main._init.getResX();
static int resY = Main._init.getResY();
static String font = Main._init.getOurFont();
private static int obstacleHeight;
private static int obstacleWidth;
private Image chosenImage;
public Obstacles() {
System.out.println("New obstacle object created!");
setScaleIndex();
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Printed obstacle");
g.drawImage(chosenImage, 0, 0, this);
}
// Applying the scaleIndex to the ground objects X and Y dimensions.
// Size changes can be made manually in Init.
public void setScaleIndex() {
obstacleWidth = (int) (Init.getObstacleSize(0) * Init.getScaleIndex());
obstacleHeight = (int) (Init.getObstacleSize(1) * Init.getScaleIndex());
}
public int getObstacleHeight() {
return obstacleHeight;
}
public int getObstacleWidth() {
return obstacleWidth;
}
public void setObstacleImage(Image image) {
chosenImage = image;
}
}
Related
Ive made a class that applies a tiling effect to a bufferedimage and i have to make a JUnit test on the class but im unsure of what to test and how i can test it.
I know i have to use assert and the likes but im not sure how.
I was thinking it would be something with the image being divided in 4.
public class TilesAction extends AbstractSelectedAction {
public static String ID = "edit.Tiles";
SVGImageFigure image;
public TilesAction(DrawingEditor editor) {
super(editor);
}
#Override
public void actionPerformed(ActionEvent ae) {
final DrawingView view = getView();
final LinkedList<Figure> figures = new LinkedList<Figure>(view.getSelectedFigures());
final Figure figure = figures.getFirst();
tiling(figure);
fireUndoableEditHappened(new AbstractUndoableEdit() {
#Override
public String getPresentationName() {
return labels.getTextProperty(ID);
}
#Override
public void redo() throws CannotRedoException {
super.redo();
}
#Override
public void undo() throws CannotUndoException {
super.undo();
}
});
}
private void tiling(Figure figure) {
image = (SVGImageFigure) figure;
BufferedImage tilingImage = image.getBufferedImage();
Graphics g = tilingImage.getGraphics();
DefaultDrawingView ddv = new DefaultDrawingView();
int width = tilingImage.getWidth() / 4;
int height = tilingImage.getHeight() / 4;
Image scaled = tilingImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
// Tile the image to fill our area.
for (int x = 0; x < tilingImage.getWidth(); x += width) {
for (int y = 0; y < tilingImage.getHeight(); y += height) {
g.drawImage(scaled, x, y, null);
}
}
g.dispose();
}
}
I have a large program that I will post some classes of and hopefully you guys can find the problem. Basically, sometimes when I start it, it creates the game just fine, and others the background is up a few pixels to the north and west directions leaving very unsightly whitespace. I cannot seem to find the missing piece of code that decides whether not it does this. It honestly feel like some kind of rendering glitch on my machine. At any rate, I have put a background getX and getY method in for debugging and have noticed that whether the background is fully stretched to the screen(its a custom background so the pixel height and width match perfectly), or its up and to the left, the background still reads that it is displaying at (0,0). I will post all the methods from the main thread to the creating of the background in the menu. I will leave notes indicating the path it takes through this code that gets it to creating the background. Thank you for your help and I will check in regularly for edits and more information.
EDIT: added background.java
EDIT2: added pictures explaining problem
Menu.java *ignore the FileIO code, the main point is the creation of a new GamePanel()
public class Menu {
private static File file;
public static void main(String[] args) throws IOException {
file = new File("saves.txt");
if(file.exists()){
FileIO.run();
FileIO.profileChoose();
}
else{
FileIO.profileCreate();
FileIO.run();
}
JFrame window = new JFrame("Jolly Jackpot Land");
window.setContentPane(new GamePanel());
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
Next is the GamePanel.java
public class GamePanel extends JPanel implements Runnable, KeyListener {
// ID
private static final long serialVersionUID = 1L;
// Dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// Thread
private Thread thread;
private boolean running;
private int FPS = 30;
private long targetTime = 1000 / FPS;
// Image
private BufferedImage image;
private Graphics2D g;
// Game State Manager
private GameStateManager gsm;
public GamePanel() {
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
private void init() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
gsm = new GameStateManager();
}
#Override
public void run() {
init();
long start;
long elapsed;
long wait;
// Game Loop
while (running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - (elapsed / 1000000);
if (wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void update() {
gsm.update();
}
private void draw() {
gsm.draw(g);
}
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
g2.dispose();
}
#Override
public void keyPressed(KeyEvent k) {
gsm.keyPressed(k.getKeyCode());
}
#Override
public void keyReleased(KeyEvent k) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
This calls for the creation of a new GameStateManager object in its init() method and the class for that is here.
GameStateManager.java
public class GameStateManager {
private ArrayList<GameState> gameStates;
private int currentState;
public static final int MENUSTATE = 0;
public static final int SLOTGAMESTATE = 1;
public static final int DICEGAMESTATE = 2;
public static final int ROULETTEGAMESTATE = 3;
public static final int LEADERBOARDSTATE = 4;
public static final int SETTINGSSTATE = 5;
public static final int HELPSTATE = 6;
public GameStateManager() {
gameStates = new ArrayList<GameState>();
currentState = 0;
gameStates.add(new MenuState(this));
gameStates.add(new SlotGameState(this));
gameStates.add(new DiceGameState(this));
gameStates.add(new RouletteGameState(this));
gameStates.add(new LeaderboardState(this));
gameStates.add(new SettingsState(this));
gameStates.add(new HelpState(this));
}
public void setState(int state){
currentState = state;
gameStates.get(currentState).init();
currentState = 0;
}
public int getState() {
return currentState;
}
public void update() {
gameStates.get(currentState).init();
}
public void draw(java.awt.Graphics2D g){
gameStates.get(currentState).draw(g);
}
public void keyPressed(int k){
gameStates.get(currentState).keyPressed(k);
}
public void keyReleased(int k) {
gameStates.get(currentState).keyReleased(k);
}
}
GameState is an abstract class I have so its not worth posting, it only contains init(), draw(), etc. This next class is the last and final class and is called because GameStateMananger starts at MENUSTATE or 0, and when GSM is initialized it initializes its current state, thus taking us to the class MenuState
MenuState.java
public class MenuState extends GameState {
private Background bg;
public FontMetrics fontMetrics;
private int choice = 0;
private String[] options = { "Slot Machine", "Dice Toss", "Roulette Wheel", "Leaderboards", "Settings", "Help",
"Quit" };
private Color titleColor;
private Font titleFont;
private Font font;
public MenuState(GameStateManager gsm) {
this.gsm = gsm;
try {
bg = new Background("/Backgrounds/happybg.png");
titleColor = Color.WHITE;
titleFont = new Font("Georgia", Font.PLAIN, 28);
} catch (Exception e) {
e.printStackTrace();
}
font = new Font("Arial", Font.PLAIN, 12);
}
#Override
public void init() {
}
#Override
public void update() {
}
#Override
public void draw(Graphics2D g) {
Canvas c = new Canvas();
fontMetrics = c.getFontMetrics(font);
// Draw BG
bg.draw(g);
// Draw title
g.setColor(titleColor);
g.setFont(titleFont);
String title = "Jolly Jackpot Land!";
g.drawString(title, 36, 60);
g.setFont(font);
for (int i = 0; i < options.length; i++) {
if (i == choice)
g.setColor(Color.RED);
else
g.setColor(Color.WHITE);
g.drawString(options[i], 30, 120 + i * 15);
}
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.PLAIN, 10));
g.drawString("v1.1", 165, 235);
Object[] a = { ("Name: " + Player.getName()), ("Gil: " + Player.getGil()),
("Personal Best: " + Player.getPersonalBest()), ("Winnings: " + Player.getWinnings()),
("Wins: " + Player.getWins()), ("Losses: " + Player.getLosses()),
("Win/Loss Ratio: " + String.format("%.2f", Player.getRatio()) + "%") };
g.setFont(font);
if (Player.getName() != null) {
for (int x = 0; x < a.length; x++) {
g.drawString(a[x].toString(), GamePanel.WIDTH - fontMetrics.stringWidth(a[x].toString()) - 30,
120 + x * 15);
}
}
}
private void select() {
if (choice == 0) {
// Slots
gsm.setState(GameStateManager.SLOTGAMESTATE);
}
if (choice == 1) {
// Dice
gsm.setState(GameStateManager.DICEGAMESTATE);
}
if (choice == 2) {
// Roulette
gsm.setState(GameStateManager.ROULETTEGAMESTATE);
}
if (choice == 3) {
// Leaderboards
gsm.setState(GameStateManager.LEADERBOARDSTATE);
}
if (choice == 4) {
// Settings
gsm.setState(GameStateManager.SETTINGSSTATE);
}
if (choice == 5) {
// Help
gsm.setState(GameStateManager.HELPSTATE);
}
if (choice == 6) {
// Quit
System.exit(0);
}
}
#Override
public void keyPressed(int k) {
if (k == KeyEvent.VK_ENTER) {
select();
}
if (k == KeyEvent.VK_UP) {
choice--;
if (choice == -1) {
choice = options.length - 1;
}
}
if (k == KeyEvent.VK_DOWN) {
choice++;
if (choice == options.length) {
choice = 0;
}
}
}
#Override
public void keyReleased(int k) {
}
}
Background.java
public class Background {
private BufferedImage image;
private double x;
private double y;
public Background(String s) {
try {
image = ImageIO.read(getClass().getResourceAsStream(s));
} catch (Exception e) {
e.printStackTrace();
}
}
public void setPosition(double x, double y) {
this.setX(x);
this.setY(y);
}
public void draw(Graphics2D g) {
g.drawImage(image, 0, 0, null);
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
This is where it waits for input in the game loop basically. I know this is a lot of code, but a lot of it is skimming till a method call takes you to the next class. I just can't figure out why it only happens sometimes, if it was consistent I could debug it. Any help would be extremely appreciated.
These are both from clicking the .jar of the above program, exact same .jar, exact same source code, different result. I am bewildered.
I'm coding a game for my final project in Java, our teacher provided us with a Board class that is a component that allows us to place and remove pegs on a virtual game board instead of having to code one ourselves. I'm trying to add Key Binding to the Board component but the action I want performed on key press is happening when I run the program but It won't run when I type a Key.
The board class already has a method for getting the position clicked on the component and I think this might be interfering with my Code but I'm not sure.
This is my game class where I tried to add keybinding
package rpgGame;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class RPGGame
{
public static final GameWorld WORLD_MAP = new GameWorld();
public static Board LOCAL_MAP = new Board(20,50);
public static List<Mobile> allMobs = new ArrayList<Mobile>();
public static final Player PLAYER = new Player();
public static int xIndex = ((GameWorld.WORLD_SIZE-1)/2) - (50/2);
public static int yIndex = ((GameWorld.WORLD_SIZE-1)/2) - (20/2);
public static boolean boardUpdate = true;
public enum Direction {RIGHT,LEFT,UP,DOWN}
private static final String MOVE_PLAYER_UP = "move up";
private static final String MOVE_PLAYER_LEFT = "move left";
private static final String MOVE_PLAYER_RIGHT = "move right";
private static final String MOVE_PLAYER_DOWN = "move down";
public static final Thread SYNC_BOARD = new Thread()
{
public synchronized void run()
{
while (boardUpdate)
{
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 20; j++)
{
if (WORLD_MAP.isOccupied(i+xIndex, j+yIndex))
{
LOCAL_MAP.putPeg(Color.RED, j, i);
System.out.println("Successfully Updated");
}
else
{
LOCAL_MAP.putPeg(Color.GRAY, j,i);
}
}
}
boardUpdate = false;
}
}
};
public RPGGame()
{
generateMobs(200);
placeMobs();
placePlayer();
SYNC_BOARD.run();
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_PLAYER_UP);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), MOVE_PLAYER_LEFT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), MOVE_PLAYER_RIGHT);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), MOVE_PLAYER_DOWN);
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_UP, new MoveAction(Direction.UP));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_LEFT, new MoveAction(Direction.LEFT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_RIGHT, new MoveAction(Direction.RIGHT));
LOCAL_MAP.getActionMap().put(MOVE_PLAYER_DOWN, new MoveAction(Direction.DOWN));
}
public static void main(String[] args)
{
new RPGGame();
}
public static void generateMobs(int numOfMobs)
{
for (int i=0; i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void generateMobs()
{
int numOfMobs = (int)(Math.random()*500);
for (int i=0;i<numOfMobs; i++)
{
allMobs.add(new Mobile());
}
}
public static void placeMobs()
{
for (int i=0; i<allMobs.size(); i++)
{
//i is used as a placeholder value for points until I create a random number generator.
WORLD_MAP.placeCharacter(i, i,allMobs.get(i));
allMobs.get(i).setLocation(i, i);
}
}
public static void placePlayer()
{
WORLD_MAP.placeCharacter(249, 249, PLAYER);
PLAYER.setLocation(249, 249);
}
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction
{
Direction direction;
public MoveAction(Direction direction)
{
if (direction.equals(Direction.RIGHT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x+1, y, x, y);
PLAYER.move(1, 0);
boardUpdate = true;
System.out.println("MOVE RIGHT");
}
if (direction.equals(Direction.LEFT))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x-1, y);
PLAYER.move(-1, 0);
boardUpdate = true;
System.out.println("MOVE LEFT");
}
if (direction.equals(Direction.UP))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y+1);
PLAYER.move(0, 1);
boardUpdate = true;
System.out.println("MOVE UP");
}
if (direction.equals(Direction.DOWN))
{
int x = PLAYER.getX();
int y = PLAYER.getY();
WORLD_MAP.moveCharacter(x, y, x, y-1);
PLAYER.move(0, -1);
boardUpdate = true;
System.out.println("MOVE DOWN");
}
}
#Override
public void actionPerformed(ActionEvent e)
{
}
}
}
This is the Board class
package rpgGame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/** Board GUI for implementation with various games
* Author: Kirill Levin, Troy Vasiga, Chris Ingram
*/
#SuppressWarnings("serial")
public class Board extends JPanel
{
private static final int X_DIM = 60;
private static final int Y_DIM = 60;
private static final int X_OFFSET = 30;
private static final int Y_OFFSET = 30;
private static final double MIN_SCALE = 0.25;
private static final int GAP = 10;
private static final int FONT_SIZE = 16;
// Grid colours
private static final Color GRID_COLOR_A = new Color(153,255,102);
private static final Color GRID_COLOR_B = new Color(136,255,77);
private Color[][] grid;
private Point lastClick; // How the mouse handling thread communicates
// to the board where the last click occurred
private String message = "";
private int numLines = 0;
private double[][] line = new double[4][100]; // maximum number of lines is 100
private int columns, rows;
private int originalWidth;
private int originalHeight;
private double scale;
/** A constructor to build a 2D board.
*/
public Board (int rows, int columns)
{
super( true );
JFrame boardFrame = new JFrame( "Board game" );
this.columns = columns;
this.rows = rows;
originalWidth = 2*X_OFFSET+X_DIM*columns;
originalHeight = 2*Y_OFFSET+Y_DIM*rows+GAP+FONT_SIZE;
this.setPreferredSize( new Dimension( originalWidth, originalHeight ) );
boardFrame.setResizable(true);
this.grid = new Color[columns][rows];
this.addMouseListener(
new MouseInputAdapter()
{
/** A method that is called when the mouse is clicked
*/
public void mouseClicked(MouseEvent e)
{
int x = (int)e.getPoint().getX();
int y = (int)e.getPoint().getY();
// We need to by synchronized to the parent class so we can wake
// up any threads that might be waiting for us
synchronized(Board.this)
{
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
// Subtract one from high end so clicks on the black edge
// don't yield a row or column outside of board because of
// the way the coordinate is calculated.
if (x >= curX && y >= curY && x < nextX && y < nextY)
{
lastClick = new Point(y,x);
// Notify any threads that would be waiting for a mouse click
Board.this.notifyAll() ;
} /* if */
} /* synchronized */
} /* mouseClicked */
} /* anonymous MouseInputAdapater */
);
boardFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
boardFrame.setContentPane( this );
boardFrame.pack();
boardFrame.setVisible(true);
}
/** A constructor to build a 1D board.
*/
public Board (int cols)
{
this(1, cols);
}
private void paintText(Graphics g)
{
g.setColor( this.getBackground() );
g.setFont(new Font(g.getFont().getFontName(), Font.ITALIC+Font.BOLD, (int)(Math.round(FONT_SIZE*scale))));
int x = (int)Math.round(X_OFFSET*scale);
int y = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale + GAP ) ;
g.fillRect(x,y, this.getSize().width, (int)Math.round(GAP+FONT_SIZE*scale) );
g.setColor( Color.black );
g.drawString(message, x, y + (int)Math.round(FONT_SIZE*scale));
}
private void paintGrid(Graphics g)
{
for (int i = 0; i < this.grid.length; i++)
{
for (int j = 0; j < this.grid[i].length; j++)
{
if ((i%2 == 0 && j%2 != 0) || (i%2 != 0 && j%2 == 0))
g.setColor(GRID_COLOR_A);
else
g.setColor(GRID_COLOR_B);
int curX = (int)Math.round((X_OFFSET+X_DIM*i)*scale);
int curY = (int)Math.round((Y_OFFSET+Y_DIM*j)*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*(i+1))*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*(j+1))*scale);
int deltaX = nextX-curX;
int deltaY = nextY-curY;
g.fillRect( curX, curY, deltaX, deltaY );
Color curColour = this.grid[i][j];
if (curColour != null) // Draw pegs if they exist
{
g.setColor(curColour);
g.fillOval(curX+deltaX/4, curY+deltaY/4, deltaX/2, deltaY/2);
}
}
}
((Graphics2D) g).setStroke( new BasicStroke(0.5f) );
g.setColor(Color.BLACK);
int curX = (int)Math.round(X_OFFSET*scale);
int curY = (int)Math.round(Y_OFFSET*scale);
int nextX = (int)Math.round((X_OFFSET+X_DIM*grid.length)*scale);
int nextY = (int)Math.round((Y_OFFSET+Y_DIM*grid[0].length)*scale);
g.drawRect(curX, curY, nextX-curX, nextY-curY);
}
private void drawLine(Graphics g)
{
for (int i =0; i < numLines; i++ )
{
((Graphics2D) g).setStroke( new BasicStroke( 5.0f*(float)scale) );
g.drawLine( (int)Math.round((X_OFFSET+X_DIM/2.0+line[0][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[1][i]*Y_DIM)*scale),
(int)Math.round((X_OFFSET+X_DIM/2.0+line[2][i]*X_DIM)*scale),
(int)Math.round((Y_OFFSET+Y_DIM/2.0+line[3][i]*Y_DIM)*scale) );
}
}
/**
* Convert a String to the corresponding Color defaulting to Black
* with an invald input
*/
/*private Color convertColour( String theColour )
{
for( int i=0; i<COLOUR_NAMES.length; i++ )
{
if( COLOUR_NAMES[i].equalsIgnoreCase( theColour ) )
return COLOURS[i];
}
return DEFAULT_COLOUR;
}*/
/** The method that draws everything
*/
public void paintComponent( Graphics g )
{
this.setScale();
this.paintGrid(g);
this.drawLine(g);
this.paintText(g);
}
public void setScale()
{
double width = (0.0+this.getSize().width) / this.originalWidth;
double height = (0.0+this.getSize().height) / this.originalHeight;
this.scale = Math.max( Math.min(width,height), MIN_SCALE );
}
/** Sets the message to be displayed under the board
*/
public void displayMessage(String theMessage)
{
message = theMessage;
this.repaint();
}
/** This method will save the value of the colour of the peg in a specific
* spot. theColour is restricted to
* "yellow", "blue", "cyan", "green", "pink", "white", "red", "orange"
* Otherwise the colour black will be used.
*/
public void putPeg(Color colour, int row, int col)
{
this.grid[col][row] = colour;
this.repaint();
}
/** Same as putPeg above but for 1D boards
*/
public void putPeg(Color colour, int col)
{
this.putPeg(colour, 0, col );
}
/** Remove a peg from the gameboard.
*/
public void removePeg(int row, int col)
{
this.grid[col][row] = null;
repaint();
}
/** Same as removePeg above but for 1D boards
*/
public void removePeg(int col)
{
this.grid[col][0] = null;
repaint();
}
/** Draws a line on the board using the given co-ordinates as endpoints
*/
public void drawLine(double row1, double col1, double row2, double col2)
{
this.line[0][numLines]=col1;
this.line[1][numLines]=row1;
this.line[2][numLines]=col2;
this.line[3][numLines]=row2;
this.numLines++;
repaint();
}
/** Removes one line from a board given the co-ordinates as endpoints
* If there is no such line, nothing happens
* If multiple lines, all copies are removed
*/
public void removeLine(int row1, int col1, int row2, int col2)
{
int curLine = 0;
while (curLine < this.numLines)
{
// Check for either endpoint being specified first in our line table
if ( (line[0][curLine] == col1 && line[1][curLine] == row1 &&
line[2][curLine] == col2 && line[3][curLine] == row2) ||
(line[2][curLine] == col1 && line[3][curLine] == row1 &&
line[0][curLine] == col2 && line[1][curLine] == row2) )
{
// found a matching line: overwrite with the last one
numLines--;
line[0][curLine] = line[0][numLines];
line[1][curLine] = line[1][numLines];
line[2][curLine] = line[2][numLines];
line[3][curLine] = line[3][numLines];
curLine--; // perhaps the one we copied is also a match
}
curLine++;
}
repaint();
}
/** Waits for user to click somewhere and then returns the click.
*/
public Point getClick()
{
Point returnedClick = null;
synchronized(this) {
lastClick = null;
while (lastClick == null)
{
try {
this.wait();
} catch(Exception e) {
// We'll never call Thread.interrupt(), so just consider
// this an error.
e.printStackTrace();
System.exit(-1) ;
} /* try */
}
int x = (int)Math.floor((lastClick.getY()-X_OFFSET*scale)/X_DIM/scale);
int y = (int)Math.floor((lastClick.getX()-Y_OFFSET*scale)/Y_DIM/scale);
// Put this into a new object to avoid a possible race.
returnedClick = new Point(x,y);
}
return returnedClick;
}
/** Same as getClick above but for 1D boards
*/
public double getPosition()
{
return this.getClick().getY();
}
public int getColumns()
{
return this.columns;
}
public int getRows()
{
return this.rows;
}
}
You're shooting yourself in the foot with that thread code -- you're calling run() not start() on it
SYNC_BOARD.run();
This will run on the Swing event thread and risks completely freezing your GUI.
As a general rule, you should almost never extend Thread but rather implement Runnable, but regardless, don't use that Thread code -- Instead use a Swing Timer since your code has no breaks in it, no Thread.sleeps and it will make your CPU awfully busy, and the Swing Timer will help make sure that your code obeys Swing threading rules.
Also your MoveAction is wrong. Most of that code should go in the actionPerformed method. The constructor should just set the direction field and that's it.
Something like:
#SuppressWarnings("serial")
public class MoveAction extends AbstractAction {
Direction direction;
public MoveAction(Direction direction) {
// this is the only code the constructor should have!
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
// use direction to help make move in here
}
}
Understand that this is likely causing some major problems, since the constructor is called on program creation (hence your key bindings "work" when the program starts), but it's the actionPerformed that actually gets called when the right key is pressed.
I have just started on learning how to use keyBindings and I cannot find out what I am doing wrong as when I press the UP arrow on my keyboard it does nothing.
My Main Game Window
public class GameWindow extends JFrame{
private static final long serialVersionUID = 1L;
public int WIDTH = 160, HEIGHT = WIDTH/12 *9, SCALE = 3;
public boolean running = false;
BackGround bg = new BackGround();
Ranger R = new Ranger();
TimerClass T = new TimerClass();
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH * SCALE, HEIGHT * SCALE);
setResizable(false);
running = true;
add(bg);
bg.add(R);
bg.setFocusable(true);
R.setFocusable(true);
setFocusable(true);
setVisible(true);
bg.repaint();
run();
}
public void run() {
while (running) {
render();
}
}
public void render() {
bg.setLocation(Ranger.bgX, Ranger.bgY);
R.setLocation(Ranger.X, Ranger.Y);
R.setIcon(Ranger.rangerA[Ranger.I]);
R.repaint();
bg.repaint();
}
}
And My Ranger Class
public class Ranger extends JLabel {
private static final long serialVersionUID = 1L;
public static int X, Y, dX, dY, bgX, bgY, I = 0, jumpTime = 100;
public static boolean moving = false, movingLeft = false,
movingRight = false, onFloor = false, jumping = false,
movingUp = false, movingDown = false;
public int totalImages = 6;
public BufferedImage ranger1, ranger2, ranger3, ranger4, ranger5, ranger6;
public static ImageIcon[] rangerA;
static TileMap TileMap = new TileMap();
public Ranger() {
try {
// not moving
ranger1 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger0.png"));
ranger2 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger1.png"));
// moving Left
ranger3 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger2.png"));
ranger4 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger3.png"));
// moving Right
ranger5 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger4.png"));
ranger6 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger5.png"));
} catch (IOException e) {
e.printStackTrace();
}
array();
}
public void array() {
rangerA = new ImageIcon[6];
{
rangerA[0] = new ImageIcon(ranger1);
rangerA[1] = new ImageIcon(ranger2);
rangerA[2] = new ImageIcon(ranger3);
rangerA[3] = new ImageIcon(ranger4);
rangerA[4] = new ImageIcon(ranger5);
rangerA[5] = new ImageIcon(ranger6);
}
}
public void move() {
AbstractAction moveUp = new AbstractAction() {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
System.out.println("Move up");
}
};
this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "moveUp");
this.getActionMap().put("moveUp", moveUp);
X += dX;
Y += dY;
dX = 0;
dY = 0;
if (movingRight || movingLeft) {
moving = true;
}
}
}
I am trying it to output that it will move up in the console so that i can lean how to make new KeyBindings but I don't know why it dosn't work. Any solutions/tips will be much appreciated.
P.S. I am new to Java so sorry for simple mistakes and I am also aware of the wild loop in the main game window class.
EDIT: move() is called every few miliseconds in a seperate timer class.
KeyStroke.getKeyStroke("W") doesn't do what you think it does. Using this form requires a verbose description of the action, ie typed w.
For this reason, I never use it. Instead, I prefer to use KeyStroke.getKeyStroke(KeyEvent.VK_W, 0) which is much more direct
See KeyStroke#getKeyStroke(int, int) for more details.
You're also not binding the key strokes as you never call the move method. Instead, bind the key strokes in the classes constructor or some other method which should be called only once.
I am trying to move a train across my java window and am having serious problems. I have a Train class in which I made the train, and a Driver class which is supposed to move the train. I need to make the whole train move from right to left until it 'passes' the left edge of the screen. Then add an if statement to change the dx so the train restarts on the right side . The below is what I have tried but it is not working. Can anyone help me please??
public class Driver extends GraphicsProgram
{
//~ Instance/static variables .............................................
private static final int N_STEPS = 1000;
private static final int PAUSE_TIME = 20;
private static final double TRAIN_LENGTH = 320;
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* The run() method of the Driver Class.
* Creates an instance of the Train Class.
* Responsible for animating the train across the screen.
*/
public void run()
{
Train train = new Train(getGCanvas());
for (int i = 0; i < N_STEPS; i++) {
train.move(-100, 0);
pause(PAUSE_TIME);
}
Here is a little demo made with swing. Just replace the black rectangle with an image of your train and you're done.
The trick is to use a separate thread (or timer) to do the animation loop (often called game loop). The loop only tells your window to redraw itself, and on each redraw, you first compute the new positions of the animated objects, then you draw them.
import javax.swing.*;
import java.awt.*;
public class TrainDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Train Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 400);
frame.setLocationRelativeTo(null);
frame.add(new TrainCanvas());
frame.setVisible(true);
}
}
class TrainCanvas extends JComponent {
private int lastX = 0;
public TrainCanvas() {
Thread animationThread = new Thread(new Runnable() {
public void run() {
while (true) {
repaint();
try {Thread.sleep(10);} catch (Exception ex) {}
}
}
});
animationThread.start();
}
public void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
int trainW = 100;
int trainH = 10;
int trainSpeed = 3;
int x = lastX + trainSpeed;
if (x > w + trainW) {
x = -trainW;
}
gg.setColor(Color.BLACK);
gg.fillRect(x, h/2 + trainH, trainW, trainH);
lastX = x;
}
}
color c = color(0); float x = 0; float y = 100; float speed = 1;
void setup() { size(200,200); }
void draw() { background(255); move(); display(); }
void move() { x = x + speed; if (x > width) { x = 0; } }
void display() { fill(c); rect(x,y,30,10); }