Related
Tell me how to make it change to the next one when you click on a picture. I use a library "ControlP5, cp5.addbutton" with a regular button in which I could do without a picture.
I have two examples, with a regular button and a picture, where I use a different way to change the picture by hovering the mouse and clicking in order to show you clearly.
"minutesss1" - a regular button with public void minutesss1() which uses .setCaptionLabel("ВЫКЛ").
"minutesss2" - How I think to use for the picture .setCaptionLabel("ВЫКЛ") / Not required ?????
Complete example code:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700,400);
PFont fontn = createFont("Times New Roman",18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman",18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200,200)
.setSize(99,25);
PImage[] imgs1 = {loadImage("0.png"),loadImage("1.png"),loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400,200)
.setImages(imgs1);
minutess2.setSize(99,25);
textFont(fontn);}
public void minutesss1() {
min+=10;
if (min>60) {min=0; minutess1.setCaptionLabel("ВЫКЛ"); }
else minutess1.setCaptionLabel(str(min)+" Мин");}
void draw(){
background(0);
fill(255);}
It's great that you've posted the code formatted like that.
It would be even better if you format it in Processing (Ctrl + T) before to make it easier to read.
It is difficult to fully understand what your asking.
If you're using a translation tool you can try breaking long phrases into smaller, simpler sentences. Hopefully the translation tool will do a better job.
From what I can understand there are two questions here:
How can you call the same minutes updating function that works for the first button from the second button ?
How can you use custom images to skin the second button ?
The first question can be tackled multiple ways.
Here are a couple of options:
Option 1: Use controlEvent which gets called automatically when updating any controlP5 component. You can check which button was pressed and call a function accordingly:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700, 400);
PFont fontn = createFont("Times New Roman", 18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman", 18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200, 200)
.setSize(99, 25);
//PImage[] imgs1 = {loadImage("0.png"), loadImage("1.png"), loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
PImage[] imgs1 = {getImage(99,25,color(0,0,192)),
getImage(99,25,color(0,0,240)),
getImage(99,25,color(0,0,120))};
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400, 200)
.setImages(imgs1);
minutess2.setSize(99, 25);
textFont(fontn);
}
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
public void minutesss1() {
min+=10;
if (min>60) {
min=0;
minutess1.setCaptionLabel("ВЫКЛ");
} else minutess1.setCaptionLabel(str(min)+" Мин");
println(min,minutess1.getCaptionLabel().getText());
}
void draw() {
background(0);
fill(255);
}
public void controlEvent(ControlEvent event) {
if(event.controller() == minutess2){
minutesss1();
}
}
Option 2: Extract the instructions of the first button press function into a separate function that can be called by both. This is probably simpler and more intuitive to read:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700, 400);
PFont fontn = createFont("Times New Roman", 18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman", 18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200, 200)
.setSize(99, 25);
//PImage[] imgs1 = {loadImage("0.png"), loadImage("1.png"), loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
// don't have images to reproduce: making new ones
PImage[] imgs1 = {getImage(99,25,color(0,0,192)),
getImage(99,25,color(0,0,240)),
getImage(99,25,color(0,0,120))};
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400, 200)
.setImages(imgs1);
minutess2.setSize(99, 25);
textFont(fontn);
}
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
void updateMinutes(){
min+=10;
if (min>60) {
min=0;
minutess1.setCaptionLabel("ВЫКЛ");
} else minutess1.setCaptionLabel(str(min)+" Мин");
println(min,minutess1.getCaptionLabel().getText());
}
public void minutesss1() {
updateMinutes();
}
public void minutesss2() {
updateMinutes();
}
void draw() {
background(0);
fill(255);
}
Regarding the second part of your question it's unclear if you want to have images just for the default controlP5 states(default, over, active, highlight) using setImages() or not. If you pass more than 4 or less than 3 images they will be ignored as you can see in the source code
If you want to display a different image for each minutes update (e.g. off, 10 , 20, 30, 40, 50, 60) then you will need to make your own custom button.
The logic isn't that complicated and you can use the Button Example as a simpler starting point.
It would be great to encapsulate that more complex custom functionality and you'd need a few Object Oriented Programming (OOP) basics for that. You can check out the Objects tutorial and example
For illustration purposes here's a separate sketch that would display a different image for each of the button press states (ignoring the over/highlight states):
ImageButton button;
void setup(){
size(300, 300);
// button dimensions
int w = 75;
int h = 25;
// test with generated images
button = new ImageButton(112, 137, w, h,
new PImage[]{
// use loadImage with your own images instead of getImage :)
getImage(w, h, color(192, 0, 32 * 2)), // off
getImage(w, h, color(0, 0, 32 * 3)), // 10
getImage(w, h, color(0, 0, 32 * 4)), // 20
getImage(w, h, color(0, 0, 32 * 5)), // 30
getImage(w, h, color(0, 0, 32 * 6)), // 40
getImage(w, h, color(0, 0, 32 * 7)), // 50
getImage(w, h, color(0, 0, 32 * 8)), // 60
});
// loading images will be something similar to:
//button = new ImageButton(112, 137, w, h,
// new PImage[]{
// loadImage("0.png"), // off
// loadImage("1.png"), // 10
// loadImage("2.png"), // 20
// loadImage("3.png"), // 30
// loadImage("4.png"), // 40
// loadImage("5.png"), // 50
// loadImage("6.png"), // 60
// });
}
void draw(){
background(0);
button.draw();
}
void mousePressed(){
button.mousePressed(mouseX,mouseY);
println(button.min);
}
// test images to represent loaded state images
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
// make a custom image button class
class ImageButton{
// minutes is the data it stores
int min = 0;
// images for each state
PImage[] stateImages;
// which image to display
int stateIndex;
// position
int x, y;
// dimensions: width , height
int w, h;
// text to display
String label = "ВЫКЛ";
ImageButton(int x, int y, int w, int h, PImage[] stateImages){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stateImages = stateImages;
}
void mousePressed(int mx, int my){
// check the cursor is within the button bounds
boolean isOver = ((mx >= x && mx <= x + w) && // check horizontal
(my >= y && my <= y + h) ); // check vertical
if(isOver){
min += 10;
stateIndex++;
if (min>60) {
min = 0;
stateIndex = 0;
label = "ВЫКЛ";
} else {
label = min + " Мин";
}
}
}
void draw(){
// if the images and index are valid
if(stateImages != null && stateIndex < stateImages.length){
image(stateImages[stateIndex], x, y, w, h);
}else{
println("error displaying button state image");
println("stateImages: ");
printArray(stateImages);
println("stateIndex: " + stateIndex);
}
// display text
text(label, x + 5, y + h - 8);
}
}
I'm trying to make a simple guess the number game and when the user guesses a number between a specific range from the actual answer it will draw a rectangle with a different colour. As of now i'm just testing and have created 2 paint methods and want to now how to call the method "paint2".
import java.awt.*;
import hsa.Console;
import javax.swing.JFrame;
import java.util.Random;
import java.awt.Canvas;
import java.awt.Graphics;
public class MashGuessTheNumber extends Canvas {
static Console c; // The output console
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("My Drawing");
Canvas canvas = new MashGuessTheNumber();
canvas.setSize(400, 400);
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
MashGuessTheNumber sm = new MashGuessTheNumber();
c = new Console();
int loop = 0;
while (loop == 0) { // loop used to continue looping the questions after one is answered
int answer = 0;
c.println("Welcome to the guess the number game!");
c.println("What is your name?");
String name = c.readLine();
c.print("why, hello there ");
c.println(name);
c.println("What diffuculty would you like to play?(easy/medium/hard)");
String diff = c.readLine();
if (diff.equalsIgnoreCase("easy")) {
// *Location of random number generator*
c.println("So you chose easy, huh");
c.println("I'm thinking of a number between 1 and 10");
int guess = 1;
answer = (int) (Math.random() * ((10 - 1) + 1));
while (guess != answer) {
c.println("What is it?");
guess = c.readInt();
c.println(answer);
if ((((guess - answer) < 3) && ((guess - answer) > 0))
|| (((answer - guess) < 3) && ((answer - guess) > 0))) {
c.println("EXTREMELY HOT");
}
}
if (guess == answer) {
c.println("You did it!");
}
}
if (diff.equalsIgnoreCase("medium")) {
// *Location of random number generator*
c.println("So you chose medium, huh");
c.println("I'm thinking of a number between 1 and 100");
c.println("What is it?");
String guess = c.readLine();
answer = (int) (Math.random() * ((100 - 1) + 1));
}
if (diff.equalsIgnoreCase("hard")) {
// *Location of random number generator*
c.println("So you chose hard, huh");
c.println("I'm thinking of a number between 1 and 1000");
c.println("What is it?");
String guess = c.readLine();
answer = (int) (Math.random() * ((1000 - 1) + 1));
}
c.println("Would you like to play again?(y/n)"); // if answerd with "y" the loop will repeat
String cont = c.readLine();
if (cont.equalsIgnoreCase("y")) {
loop = 0;
} else {
for(int i=1;i<=24;i++){
c.println(" ");
c.setCursor(12, 30);
c.println("See you next time. Bye!");
loop = 1; // Stops the loop and says bye to the user
}
}
// Place your program here. 'c' is the output console
}
public void paint(Graphics g) {
int x[] = { 35, 75, 75, 35 };
int y[] = { 10, 10, 200, 200 };
g.setColor(Color.black);
int numberofpoints = 4;
g.drawPolygon(x, y, numberofpoints);
}
public void paint2(Graphics g) {
int x[] = { 35, 75, 75, 35 };
int y[] = { 10, 10, 200, 200 };
g.setColor(Color.blue);
int numberofpoints = 4;
g.drawPolygon(x, y, numberofpoints);
}
// main method
} // MashGuessTheNumber class
I'm just trying to draw a different rectangle as of now over top the first one whenever I want and if there is another way that doesn't use two methods that would also be helpful
I'm not seeing where you call paint() at all, but I would make a second parameter that would act as a flag telling me whether to draw a blue or black rectangle. This would get rid of repeated code.
public void paint (Graphics g, Color color)
{
int x[] = {35, 75, 75, 35};
int y[] = {10, 10, 200, 200};
g.setColor (color);
int numberofpoints = 4;
g.drawPolygon (x, y, numberofpoints);
}
I don't know if 'Color' is the correct object to use to pass in a color, but the point is to pass in something that will help you choose what color to make the rectangle so that way you don't have to write two very similar methods. You will call paint() like this:
if(some condition) {
paint(g, Color.blue);
}
else {
paint(g, Color.black);
}
In the Java Swing framework, you don't call Canvas.paint() yourself. But, you can use your own intance variables.
If you add an instance variable to your MashGuessNumber class
private Color myColor = Color.BLACK;
Then modify your if statement
if (count == answer) {
System.out.println("You did it1);
myColor == Color.BLUE;
repaint(); // already a Canvas method
}
Then modify your paint() method
public void paint (Graphics g, Color color)
{
int x[] = {35, 75, 75, 35};
int y[] = {10, 10, 200, 200};
g.setColor (myColor);
int numberofpoints = 4;
g.drawPolygon (x, y, numberofpoints);
}
This should do the trick.
The call to repaint() tells Swing to redraw the canvas by calling your paint() method. Otherwise the system would have no way of knowing that your Canvas needs to be redrawn.
Stack Trace Hi i'm working on a GUI for monopoly and i've reached a stage where i need to get user input that would ask for a specific number of players and would provide a certain number of tokens (monopoly pieces) based on the number of players specified. I've currently hit a problem i feel really shouldn't be as serious as it is where if i do try to specify the number of players for the game i'm hit with a an ArrayIndexOutOfBoundsException and i can't seem to identify where or what is wrong and how to fix it. Thanks for the help in advance and any edits or tips for my code to make it more efficient are also welcome. I will attach all necessary program files.
P.s i posted the full source code as i felt it was necessary as a shortened version wouldn't make things as clear. Thanks again. P.p.s the code does work when i specify the number of players in the source code without asking for user input but stops working when i do specify user input. Monopoly Board Image
package sprint_One;
/*
* Code written by: lagosBoys A layered pane was used to place all components on the frame at the
* desired locations
*/
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
public class UI_Monopoly_Board extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
// Array of coordinates for the position of each square on the board
Point[] locations = {new Point(630, 643), new Point(570, 643), new Point(510, 643),
new Point(450, 643), new Point(390, 643), new Point(330, 643), new Point(270, 643),
new Point(210, 643), new Point(150, 643), new Point(95, 643), new Point(60, 643),
new Point(60, 573), new Point(60, 503), new Point(60, 433), new Point(60, 383),
new Point(60, 323), new Point(60, 273), new Point(60, 213), new Point(60, 153),
new Point(60, 93), new Point(60, 33),
new Point(120, 13), new Point(180, 13), new Point(230, 13), new Point(280, 13),
new Point(340, 13), new Point(400, 13), new Point(460, 13), new Point(520, 13),
new Point(580, 13), new Point(660, 60), new Point(660, 120), new Point(660, 160),
new Point(660, 220), new Point(660, 280), new Point(660, 340), new Point(660, 400),
new Point(660, 460), new Point(660, 520), new Point(660, 580), new Point(660, 640)};
// The default position or starting point which is go
Point defaultPosition = new Point(600, 603);
private int players;
private Token[] token;
private static JPanel infoPanel;
private static JPanel commandPanel;
// creates a fixed length for the text field used by the command field
final static int field_Width = 20;
private static JTextField commandField = new JTextField(field_Width);
private static JLabel commandLabel = new JLabel("Enter Command: ");
private Border blackLineBorder;
private final int ROWS = 35;
private final int COLUMNS = 40;
private JTextArea textArea = new JTextArea(ROWS, COLUMNS);
private static JLabel echoed_Text_Label = new JLabel();
private JLayeredPane layeredPane = getLayeredPane(); // The use of a JLayeredPane allows easier
// and more flexible specification of
// component positions
private static JLabel monopolyImageLabel;
public UI_Monopoly_Board() {
String playerNumber = JOptionPane.showInputDialog("Please enter the number of players");
// int tokenNumber = Integer.parseInt(playerNumber);
// players = 6;
players = Integer.parseInt(playerNumber);
int offset = 10;
// Initialise tokens depending on number of players and spaces them out with offset
if(players >= 2 || players <= 6)
{
token = new Token[players];
switch (players) {
case 2:
token[0] = new Token();
token[0].setBounds(10, 10, 700, 700);
token[0].setPosition(600, 603);
token[1] = new Token(Color.red, null);
token[1].setBounds(10, 10, 700, 700);
token[1].setPosition(600 + offset, 603 + offset);
break;
case 3:
token[0] = new Token();
token[0].setBounds(10, 10, 700, 700);
token[0].setPosition(600, 603);
token[1] = new Token(Color.red, null);
token[1].setBounds(10, 10, 700, 700);
token[1].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[2] = new Token(Color.blue, null);
token[2].setBounds(10, 10, 700, 700);
token[2].setPosition(600 + offset, 603 + offset);
break;
case 4:
token[0] = new Token();
token[0].setBounds(10, 10, 700, 700);
token[0].setPosition(600, 603);
token[1] = new Token(Color.red, null);
token[1].setBounds(10, 10, 700, 700);
token[1].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[2] = new Token(Color.blue, null);
token[2].setBounds(10, 10, 700, 700);
token[2].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[3] = new Token(Color.green, null);
token[3].setBounds(10, 10, 700, 700);
token[3].setPosition(600 + offset, 603 + offset);
break;
case 5:
token[0] = new Token();
token[0].setBounds(10, 10, 700, 700);
token[0].setPosition(600, 603);
token[1] = new Token(Color.red, null);
token[1].setBounds(10, 10, 700, 700);
token[1].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[2] = new Token(Color.blue, null);
token[2].setBounds(10, 10, 700, 700);
token[2].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[3] = new Token(Color.green, null);
token[3].setBounds(10, 10, 700, 700);
token[3].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[4] = new Token(Color.yellow, null);
token[4].setBounds(10, 10, 700, 700);
token[4].setPosition(600 + offset, 603 + offset);
break;
case 6:
token[0] = new Token();
token[0].setBounds(10, 10, 700, 700);
token[0].setPosition(600, 603);
token[1] = new Token(Color.red, null);
token[1].setBounds(10, 10, 700, 700);
token[1].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[2] = new Token(Color.blue, null);
token[2].setBounds(10, 10, 700, 700);
token[2].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[3] = new Token(Color.green, null);
token[3].setBounds(10, 10, 700, 700);
token[3].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[4] = new Token(Color.yellow, null);
token[4].setBounds(10, 10, 700, 700);
token[4].setPosition(600 + offset, 603 + offset);
offset = offset + 10;
token[5] = new Token(Color.cyan, null);
token[5].setBounds(10, 10, 700, 700);
token[5].setPosition(600 + offset, 603 + offset);
break;
default:
System.out.println("Invalid number of players");
}
}
// The location of the image should be specified here
monopolyImageLabel =
new JLabel(new ImageIcon(this.getClass().getResource("Monopoly_board.jpg")));
monopolyImageLabel.setBounds(-50, -30, 800, 750);
// The image and the tokens are added to the pane at different levels allowing them to overlap
layeredPane.add(monopolyImageLabel);
layeredPane.add(token[0], new Integer(1));
layeredPane.add(token[1], new Integer(2));
layeredPane.add(token[2], new Integer(3));
layeredPane.add(token[3], new Integer(4));
layeredPane.add(token[4], new Integer(5));
layeredPane.add(token[5], new Integer(6));
setSize(1500, 750);
setExtendedState(JFrame.MAXIMIZED_BOTH); // Sets the default window for the JFrame as a
// maximised
this.setResizable(false);
setTitle("Welcome to Monopoly");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Ensures the JFrame operation ends completely
// upon exiting the window
setVisible(true);
}
// This method displays the information panel and adds it to the pane
public void information_Panel() {
infoPanel = new JPanel();
JScrollPane scrollPane = new JScrollPane(textArea);
blackLineBorder = BorderFactory.createLineBorder(Color.BLACK);
TitledBorder title = BorderFactory.createTitledBorder(blackLineBorder, "Information Panel");
infoPanel.setBorder(title);
infoPanel.add(echoed_Text_Label, BorderLayout.NORTH);
// prevents any information from being added or deleted from the information panel.
textArea.setEditable(false);
infoPanel.add(scrollPane);
infoPanel.setBounds(750, 0, 600, 600); // specifies the desired coordinates of the panel being
// added to the layered pane
layeredPane.add(infoPanel);
}
// This method displays the command panel and adds it to the pane
public void command_Panel() {
commandPanel = new JPanel();
blackLineBorder = BorderFactory.createLineBorder(Color.BLACK);
JButton button = new JButton("Enter");
/*
* implements the actionlistener interface on the button to help execute a command when the
* button is clicked
*/
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (commandField.getText().isEmpty()) {
String command = null;
textArea.append(command);
}
else {
String command = commandField.getText();
textArea.append(command + "\n");
commandField.setText("");
}
}
});
// This invokes the actionListeners interface for actionPerformed (quick way to implement a key
// listener on the keyboards Enter button)
getRootPane().setDefaultButton(button);
commandPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
button.setPreferredSize(new Dimension(65, 20));
commandPanel.add(commandLabel);
commandPanel.add(commandField);
commandPanel.add(button);
commandPanel.setBounds(800, 630, 500, 50); // specifies the desired coordinates of the panel
// being added to the layered pane
layeredPane.add(commandPanel);
}
// Method which moves the tokens round the board one at a time
public void moveTokens() throws InterruptedException {
int i, j, offset;
offset = 0;
for (i = 0; i < token.length; i++) {
for (j = 0; j < locations.length; j++) {
token[i].setPosition(locations[j].x, locations[j].y);
repaint();
// controls the movement speed of the tokens across the board allowing for easy detection of
// their movement
Thread.sleep(300);
}
token[i].setPosition(defaultPosition.x + offset, defaultPosition.y + offset);
offset = offset + 15;
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
UI_Monopoly_Board obj = new UI_Monopoly_Board();
obj.information_Panel();
obj.command_Panel();
obj.moveTokens();
[enter image description here][1]
}
}
package sprint_One;
import java.awt.*;
import javax.swing.JComponent;
/*
* Each token has variables for location, dimension and shape, there's a constructor that allows the
* user to specify the colour of the shape the necessary accessor and mutator functions are provided
*/
public class Token extends JComponent {
/**
*
*/
private static final long serialVersionUID = 1L;
private int length;
private int breadth;
private int x;
private int y;
private Shape shape;
private Color color;
private String name;
private int balance;
public Token() {
super();
setVisible(true);
this.length = 15;
this.breadth = 15;
this.x = 5;
this.y = 5;
this.shape = new Rectangle(this.x, this.y, this.length, this.breadth);
this.color = Color.BLACK;
this.name = "";
this.balance = 20;
}
public Token(Color color, String name) {
this();
this.color = color;
this.name = name;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
// Method which specifies the x and y coordinates of the tokens
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(color);
g2.fill(shape); // fills the shape with the colour specified
g2.draw(this.shape);
}
public Shape getShape() {
// TODO Auto-generated method stub
return this.shape;
}
}
Ok, you should learn how to use a debugger...
This is the line that causes your error:
layeredPane.add(token[2], new Integer(3));
And you initialise tokenlike this:
if(players >= 2 || players <= 6)
{
token = new Token[players];
So, what if players is 2? You will get an ArrayIndexOutOfBoundsException.
Another tip:
layeredPane.add(token[0], new Integer(1));
is not as readable as
layeredPane.add(token[0], 1);
Currently i'm trying to create a stimulated annealing algorithm solving the traveling salesman problem as well as creating a gui for it. The initial cities(points) and lines display but I can't get the doSA() method to successfully run. Any ideas, sorta stumped.
class SAPanel extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
private City city,city2,city3,city4,city5,city6,city7,city8,city9,city10,city11,city12,city13,city14,city15,city16,city17,city18,city19,city20;
private int temp;
private Tour best, currentSolution;
int delay = 10;
static int POINTWIDTH = 8;
static Color POINTCOLOR = Color.MAGENTA;
static Color LINECOLOR = Color.CYAN;
Timer timer = new Timer(delay, this);
private static double coolingRate = .006;
public void start(){
temp = 10000;
timer.start();
}
public void intiSA(){
city = new City(60, 200);
TourManager.addCity(city);
city2 = new City(180, 200);
TourManager.addCity(city2);
city3 = new City(80, 180);
TourManager.addCity(city3);
city4 = new City(140, 180);
TourManager.addCity(city4);
city5 = new City(20, 160);
TourManager.addCity(city5);
city6 = new City(100, 160);
TourManager.addCity(city6);
city7 = new City(200, 160);
TourManager.addCity(city7);
city8 = new City(140, 140);
TourManager.addCity(city8);
city9 = new City(40, 120);
TourManager.addCity(city9);
city10 = new City(100, 120);
TourManager.addCity(city10);
city11 = new City(180, 100);
TourManager.addCity(city11);
city12 = new City(60, 80);
TourManager.addCity(city12);
city13 = new City(120, 80);
TourManager.addCity(city13);
city14 = new City(180, 60);
TourManager.addCity(city14);
city15 = new City(20, 40);
TourManager.addCity(city15);
city16 = new City(100, 40);
TourManager.addCity(city16);
city17 = new City(200, 40);
TourManager.addCity(city17);
city18 = new City(20, 20);
TourManager.addCity(city18);
city19 = new City(60, 20);
TourManager.addCity(city19);
city20 = new City(160, 20);
TourManager.addCity(city20);
//Initialize initial solution
currentSolution = new Tour();
currentSolution.generateIndividual();
best = currentSolution;
System.out.println("Initial solution distance: " + currentSolution.getDistance());
}
//energy represents the total distance of each tour
public static double acceptanceProbability(int energy, int newEnergy, double temp){
//if currentSolution energy is larger than newEnergy, return 1.0
if(newEnergy < energy){
return 1.0;
}
//as temp decreases, acceptance of the new solution becomes more selective
return Math.exp((energy-newEnergy) / temp);
}
public void doSA(){
//set new solution as the current solution
Tour newSolution = new Tour(currentSolution.getTour());
//get two random points on the solution
int tourPos1 = (int)(newSolution.tourSize() * Math.random());
int tourPos2 = (int)(newSolution.tourSize() * Math.random());
//get two cities depending on the selected points
City citySwap1 = newSolution.getCity(tourPos1);
City citySwap2 = newSolution.getCity(tourPos2);
//swap the cities on the selected points
newSolution.setCity(tourPos1, citySwap2);
newSolution.setCity(tourPos2, citySwap1);
int currentEnergy = currentSolution.getDistance();
int neighborEnergy = currentSolution.getDistance();
//accept new solution as the current solution if greater than random number
if(acceptanceProbability(currentEnergy, neighborEnergy, temp) > Math.random()){
currentSolution = new Tour(newSolution.getTour());
}
//keep the current solution as best if distance is greater
if(currentSolution.getDistance() > newSolution.getDistance()){
best = new Tour(currentSolution.getTour());
}
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2= (Graphics2D)(g);
g2.setStroke(new BasicStroke(3));
for(int x =0;x<19;x++){
//draw lines connecting each city in solution
g2.setColor(LINECOLOR);
g2.drawLine(best.getCity(x).getX(), best.getCity(x).getY(), best.getCity(x+1).getX(), best.getCity(x+1).getY());
g2.setColor(POINTCOLOR);
//draw all points
g2.fillOval(best.getCity(x).getX() - POINTWIDTH/2, best.getCity(x).getY() - POINTWIDTH/2, POINTWIDTH, POINTWIDTH);
}
//draw last reminding line, connecting last city to first city.
g2.setColor(LINECOLOR);
g2.drawLine(best.getCity(19).getX(), best.getCity(19).getY(), best.getCity(0).getX(), best.getCity(0).getY());
g2.setColor(POINTCOLOR);
g2.fillOval(best.getCity(0).getX() - POINTWIDTH/2, best.getCity(0).getY() - POINTWIDTH/2, POINTWIDTH, POINTWIDTH);
}
public void actionPerformed(ActionEvent e){
temp *= coolingRate -1;
if(temp >1){
doSA();
System.out.println("Final solution distance: " + best.getDistance());
System.out.println("Tour: " + best);
}
else{
((Timer)e.getSource()).stop();
}
}
}
Could be your initial parameters: Simulated Annealing is very parameter sensitive and it requires 2 parameters (initial temp and cooldown schedule), which makes it hard to tweak them. In my implementation, I've reduced it to 1 parameter by basing the cooldown on the amount of time still available.
Could also be a bug in your acceptanceProbability method. Write a unit test, which covers the corner cases, to prove it's correct. Here's my test impl.
first post here so forgive me if this is the absolute wrong thing but I'm in a tiny bit of a pickle. - My computer science class has us making our own game/program from scratch, but our teacher specifically ignored the existence of swing and said that it was out of his realm to teach it so from my readings it seems as if a game is almost out of the question, with what I've written so far my main sprite and debugging information flickers violently on key input. Is there a way around this?
I know JPanel/JFrame/Swing would fix this, but we weren't taught it and all I've read so far is confusing me to no end, if someone would be able to help me convert my code to use it that would be amazing as I learn easier from dissecting it.. Which may seem a bit dodgy but it really is the easiest way to learn from. My code so far is;
public class GameThing extends Applet implements KeyListener {
Image rightsprite, picture;
Image leftsprite, picture2;
Image spritedead, picture3;
Image background, picture4;
boolean left;
boolean dead;
boolean wall;
boolean menu;
int widthX, heightY;
int bgx, bgy;
String sr = " ";
char ch = '*';
Graphics bufferGraphics;
Image offscreen;
Dimension dim;
#Override
public void init() {
rightsprite = getImage(getDocumentBase(), "SpriteRight.png");
leftsprite = getImage(getDocumentBase(), "SpriteLeft.png");
spritedead = getImage(getDocumentBase(), "Sprite.png");
background = getImage(getDocumentBase(), "Background.png");
requestFocus();
addKeyListener(this);
left = false;
menu = true;
setSize(600, 380);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
bgx = 0;
bgy = 0;
}
public void pause(final int delay) { // PAUSE COMMAND LIFTED FROM RAY
// waits a while
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
return;
}
}
public GameThing() { // SPRITE INITIAL SPAWN LOCATION
widthX = 100;
heightY = 300;
}
#Override
public void paint(final Graphics g) { // DRAW MAIN CHARACTER SPRITE AND BACKGROUND
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
g.drawImage(offscreen, 0, 0, this);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
if (left == false) { // LEFT KEY MOVEMENT COMMANDS
g.drawImage(rightsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (left == true) { // RIGHT KEY MOVEMENT COMMANDS
g.drawImage(leftsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (dead == true) { // COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
widthX = 506;
heightY = 314;
g.drawImage(spritedead, widthX, heightY, this);
pause(500);
g.setColor(Color.white);
g.drawRect(widthX, heightY, widthX, heightY);
g.fillRect(widthX, heightY, widthX, heightY);
pause(500);
widthX = 100;
heightY = 300;
bgx = 0;
left = true;
dead = false;
}
}
#Override
public void update(final Graphics g) { // KEEPS BACKGROUND STATIC
paint(g);
}
public void keyPressed(final KeyEvent e) {
sr = "blank!";
ch = '1';
if (e.getKeyCode() == 39) {
sr = "Moving Right!";
widthX = widthX + 7;
if (widthX <= 121) {
bgx = bgx;
} else {
bgx = bgx - 7;
}
left = false;
if ((widthX > 490) && (heightY > 300)) { // FALL DEATH
sr = "You Died!";
widthX = 900;
heightY = 900;
dead = true;
}
if (widthX == 499) {
heightY = heightY + 7;
}
}
if (widthX == 2) {
wall = true;
}
if (e.getKeyCode() == 37) {
if (wall == true) {
sr = "Wall!";
if (widthX > 2) {
wall = false;
}
}
if (wall == false) {
sr = "Moving Left!";
widthX = widthX - 7;
if (bgx >= 0) {
bgx = bgx;
} else {
bgx = bgx + 7;
}
left = true;
}
}
if (e.getKeyCode() == 38) {
sr = "Jumping!";
}
repaint();
}
public void keyTyped(final KeyEvent e) {
ch = e.getKeyChar();
repaint();
}
public void keyReleased(final KeyEvent e) {
} // key released
}
ps: Jumping with the up key has been a problem too, I can't simply add to the Y variable and then subtract as it just goes so fast it's as if it never moved.. The basics of a sidescroller are a lot more than I ever expected..
I edited your code:
public class GameThing extends Applet {
// External resources
BufferedImage rightSprite, leftSprite, spriteDead, backgroundImg;
// Game data
int state = 1; //0 = left, 1 = right, 2 = dead
// Geometry
int locX = 100, locY = 300;
int bgX = 0, bgY = 0;
int groundX = 0, groundY = 334, groundW = 500, groundH = 50;
int appletW = 600, appletH = 480;
int wallW = 20, wallH = 40, wallX = 20, wallY = groundY - wallH;
final int STEP_SIZE = 7;
// Information
final String X_LOC_STR = "Sprites Current X Location is: ";
final String Y_LOC_STR = "Sprites Current Y Location is: ";
final String STATE_STR = "Currently: ";
final String X_BG_STR = "Background X location is: ";
String stateString = "";
// Double buffering
Image offscreen;
Graphics bufferGraphics;
Dimension dim;
// GUI components
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLocLabel = new Label();
Label yLocLabel = new Label();
Label stateLabel = new Label();
Label xBGLocLabel = new Label();
#Override
public void init() {
// Load images
try {
rightSprite = ImageIO.read(new File(getClass().getResource("SpriteRIght.png").getPath()));
leftSprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spriteDead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
// Set the panel that displays data
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLocLabel);
statusPanel.add(yLocLabel);
statusPanel.add(stateLabel);
statusPanel.add(xBGLocLabel);
// Create the panel that draws the game and specify its behavior
gamePanel = new Panel() {
// Reduces flickering
#Override
public void update(Graphics g) {
paint(g);
}
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.clearRect(0, 0, dim.width, dim.width);
bufferGraphics.drawImage(backgroundImg, bgX, bgY, this);
bufferGraphics.setColor(Color.BLACK); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(groundX, groundY, groundW, groundH);
bufferGraphics.setColor(Color.GREEN);
bufferGraphics.fillRect(groundX, groundY + 1, groundW, groundH);
bufferGraphics.setColor(Color.RED);
bufferGraphics.fillRect(wallX, wallY, wallW, wallH);
switch (state) {
case 0: bufferGraphics.drawImage(leftSprite, locX, locY, this);
break;
case 1: bufferGraphics.drawImage(rightSprite, locX, locY, this);
break;
case 2: bufferGraphics.drawImage(spriteDead, locX, locY, this);
// After death wait a bit and reset the game
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
pause(2000);
reset();
}
});
break;
}
g.drawImage(offscreen, 0, 0, this);
}
};
// Set the applet window
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
// Set double buffering
setSize(appletW, appletH);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
// Set the panel that draws the game
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
updateLabels();
}
// PAUSE COMMAND LIFTED FROM RAY (who is Ray?)
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Set to start parameters
public void reset() {
locX = 100; locY = 300;
bgX = 0;
state = 1;
gamePanel.repaint(); // These 2 lines automatically restart the game
updateLabels(); // Remove if you want to stay on the death screen until next press
}
public void updateLabels() {
xLocLabel.setText(X_LOC_STR + locX);
yLocLabel.setText(Y_LOC_STR + locY);
stateLabel.setText(STATE_STR + stateString);
xBGLocLabel.setText(X_BG_STR + bgX);
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
stateString = "Moving Right!";
locX += STEP_SIZE;
state = 1;
if (locX > 121)
bgX -= STEP_SIZE;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
stateString = "You Died!";
state = 2;
}
// Start fall
if (locX == 499)
locY += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX -= STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_LEFT:
stateString = "Moving Left!";
locX -= STEP_SIZE;
state = 0;
if (bgX < 0)
bgX += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX += STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_UP:
// Implement jumping
stateString = "Jumping!";
break;
}
// Repaint the game panel and update the data panel
gamePanel.repaint();
updateLabels();
}
}
}
Start by testing and understanding the changes:
I created a separate panel for the current data display and one for the game (which gets repainted).
I added a few help functions.
I made a lot of syntax changes (e.g. multiple if else replaced by switch, VK_UP instead of 37 and such).
Better to have all values in one place and read them from there instead of spreading them in various places (still needs a bit of work but it will change when advancing with the code).
Make constant values (values that don't change) final.
In Java conventions, constants are named with uppercase and underscore (_) (so Color.RED, not Color.red).
Better to use #Override when overriding methods even when the compiler understands it.
Do not block the event dispatch thread (EDT) with sleep or expensive computations. Methods like paint key events are meant to execute quickly. I used invokeLater to pause only after the EDT finished processing all queued events. (I understand that this threading subject comes as a bomb from nowhere, just bear with me).
Looking forward for the current state of the code (no new features):
state should probably be an enum or something close to it instead of random numbers.
Using Shape for drawn objects instead of specifying their parameters during painting.
Double buffering implementation notes:
You create an off-screen Image object offscreen and a Graphics object to draw on it - bufferGraphics. Insidepaint, you use only bufferGraphics for the drawings. You in fact draw on offscreen even though the paint belongs to gamePanel (or any other object and its paint), slightly confusing. When you finish drawing everything off-screen, you throw it all in one go onto the screen with g.drawImage(offscreen...) which uses gamePanel's Graphics object g.
The idea is like drawing everything on a paper and then putting it in front of the camera instead of drawing bit by bit on the camera, or something like that...
Currently, the off-screen image is the size of the whole applet and not just the game panel, but it makes little difference.
Relevant differences from a Swing approach:
Replace Label with JLabel, Panel with JPanel etc.
Swing components automatically implement double buffering (see JComponent's setDoubleBuffered and isDoubleBuffered). Remove all of those off-screen shenanigans and draw directly with Graphics g. No need to override update also.
Painting in Swing is done in paintComponent and not in paint.
Swing runs from its own thread and not from the EDT (not automatically, but easily done). There are some benefits to this I won't get into right now.
Key events are better handled by key bindings (Swing only) rather than the key listener. This manifests in responsiveness, native environment independence and more (e.g. concurrent key events).
#user1803551 Okay, so it's all sorted back to how it was again. - The scrolling background is in (Just a couple things around the place needed some tweaking from your post), and I understand that the final line "gamePanel.repaint(); is what's refreshing everything on the screen, since the background needs to be updated with every key input, it shouldn't be limited to the tiny square from your code, right? This now refreshes the whole screen, but I assume this isn't what is wanted because it just results in the flickering, the buffergraphics of the green rectangle at the bottom however don't flicker. So my assumption is to convert
g.drawImage(backgroundImg, bgx, bgy,this);
into buffergraphics?
-EDIT-
Moved the main Sprite into bufferGraphics aswell as the background image. Flickering is now almost gone, but after holding down an arrow key for a little bit the screen will flicker just for a moment and then return to normal.
public class GameTest extends Applet {
/**
*
*/
private static final long serialVersionUID = -1533028791433336859L;
Image rightsprite, leftsprite, spritedead, backgroundImg, offscreen;
boolean left = false, dead = false, wall = false, menu = true;
int locX = 100, locY = 300, width, height, bgx = 0, bgy = 0;
final int STEP_SIZE = 7;
final String X_LOC_STR= "Sprites Current X Location is: ";
final String Y_LOC_STR= "Sprites Current Y Location is: ";
final String STATE_STR= "Currently: ";
final String X_BG_STR= "Background X location is: ";
String sr = " ";
Graphics bufferGraphics;
Dimension dim;
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLoc = new Label(X_LOC_STR);
Label yLoc = new Label(Y_LOC_STR);
Label state = new Label(STATE_STR);
Label xBGLoc = new Label(X_BG_STR);
public void init() {
try {
rightsprite = ImageIO.read(new File(getClass().getResource("SpriteRight.png").getPath()));
leftsprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spritedead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
height = rightsprite.getHeight(this);
width = rightsprite.getWidth(this);
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLoc);
statusPanel.add(yLoc);
statusPanel.add(state);
statusPanel.add(xBGLoc);
gamePanel = new Panel() {
/**
*
*/
private static final long serialVersionUID = 1L;
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.drawImage(backgroundImg,bgx, bgy, this);
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
// LEFT KEY MOVEMENT COMMANDS
if (left == false)
bufferGraphics.drawImage(rightsprite, locX, locY, this);
// RIGHT KEY MOVEMENT COMMANDS
else
bufferGraphics.drawImage(leftsprite, locX, locY, this);
// COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
if (dead == true) {
locX = 506;
locY = 314;
bufferGraphics.drawImage(spritedead, locX, locY, this);
pause(500);
bufferGraphics.setColor(Color.white);
bufferGraphics.drawRect(locX, locY, width, height);
bufferGraphics.fillRect(locX, locY, width, height);
pause(500);
locX = 100;
locY = 300;
bgx = 0;
bgy = 0;
bufferGraphics.clearRect(locX, locY, width, height);
left = true;
dead = false;
}
g.drawImage(offscreen, 0, 0, this);
super.paint(g);
}
};
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
setSize(600, 480);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
}
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
sr = "blank!";
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
sr = "Moving Right!";
locX += STEP_SIZE;
if (locX > 121)
bgx = bgx - STEP_SIZE;
left = false;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
sr = "You Died!";
locX = 900;
locY = 900;
dead = true;
gamePanel.repaint();
}
if (locX == 499) {
locY += STEP_SIZE;
}
}
if (locX == 2) {
wall = true;
}
else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (wall == true) {
sr = "Wall!";
if (locX > 2) {
wall = false;
}
}
else {
sr = "Moving Left!";
locX -= STEP_SIZE;
if (bgx < 0)
bgx += STEP_SIZE;
left = true;
}
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
sr = "Jumping!";
}
xLoc.setText(X_LOC_STR + locX);
yLoc.setText(Y_LOC_STR + locY);
state.setText(STATE_STR + sr);
xBGLoc.setText(X_BG_STR + bgx);
gamePanel.repaint();
}
}
}