I have got a problem and I'm totally new at programming I have the object the Hero and I want to use all of his methods in my game class this is what I Programmed so far:
MainGame class:
class MainGame extends JComponent implements ActionListener, KeyListener{
Image Background;
MainGame() throws IOException {
Background = ImageIO.read(getClass().getResource("Background.png"));
}
public static void main (String[] args) throws IOException {
JFrame window = new JFrame("Adventure Times");
MainGame game = new MainGame();
window.add(game);
window.pack();
window.setLocationRelativeTo(null);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
window.addKeyListener(game);
}
public void paintComponent(Graphics g) {
g.drawImage(Background, 0, 0, null);
}
public Dimension getPreferredSize() { return new Dimension(800, 600);
}
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT);
Hero.moveRight();
}
public void keyReleased(KeyEvent e) {
}
}
Hero class:
public class Hero {
public int HeroX = 0;
public int HeroY = 0;
public int HeroSpeed = 0;
private BufferedImage Hero;
public Hero() {
try {
Hero = ImageIO.read(getClass().getResource("Hero.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void Draw(Graphics g) {
g.drawImage(Hero, HeroX, HeroY, null);
}
public void moveRight() {
HeroX += HeroSpeed;
}
public void moveLeft() {
HeroX -= HeroSpeed;
}
}
To use Hero's methods in your MainGame class, you either need an instance of Hero that can call them, or the method definitions have to include the static keyword. In this application, static doesn't work, and would in fact completely break your Hero class if applied to the methods you have now, so you need to instantiate a Hero. To do this, you need to, within MainGame have the line
Hero achilles = new Hero();
With the code you currently have, however, this will throw an exception in the Hero constructor that your try statement doesn't catch, as it won't be an IOException. The exception will be a result of trying to assign a value to a data type in
Hero = ImageIO.read(getClass().getResource("Hero.png"));
In fact, the code as it is won't even compile because of the attempted definition of Hero as a member of type BufferedImage. This is illegal, as you cannot use a class name as an identifier. This is similar to doing int = 4, which makes no sense. Rename the BufferedImage wherever it's referred to and the code should compile. For instance:
private BufferedImage sprite;
Alternatively, you could name it hero with a lowercase 'h' to avoid the name collision. This is also in line with naming conventions and general best practice for Java, as well as most languages. Class names are usually capitalized, while member names are usually lowercase. For more info on naming conventions, see here.
Related
So I have been looking to update one of my panels in a my client code with data that comes from a server in Indonesia. The delay is rather long (2-8) sec and Im noticing that my UI is freezing during the time it takes for the response to return from the server.
The response will be used to draw some points on a map (not yet implemented).
I have been looking all over the net to find out how to do it and I have come across:
InvokeLater.
SwingWroker.
ScheduledThreadPoolExecutor.
Making the JPanel a runnable to run in its own thread(seems like best option).
http://www.java2s.com/Tutorial/Java/0160__Thread/CreateathreadtoupdateSwing.htm
But tbh most of the data i find is out dated (more than 5 years old).
Here is the JPanel class i want to update based on a server query:
public class MapPanel extends JPanel implements Pointable, Runnable {
private static final long serialVersionUID = 1L;
private List<Shape> shapes = new LinkedList<>();
private State mapPanelState;
public Shape selected;
private BufferedImage image;
public MapPanel() {
Commander.getInstance().addShapeContainer(this);
mapPanelState = NoState.getInstance();
MouseHandler mouseHandler = new MouseHandler(this);
KeyListener keyListener = new KeyListener();
readImage();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
this.addKeyListener(keyListener);
this.setBackground(Color.white);
this.setFocusable(true);
this.requestFocusInWindow();
}
private void readImage(){
try {
image = ImageIO.read(new File("/MapCoordProject/earthmap1.jpg"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void setState(State state) {
mapPanelState = state;
}
public List<Shape> getShapes() { return shapes; }
public void setShapes(List<Shape> shapes) {
this.shapes = shapes;
}
public Shape getLastShape(){ return shapes.get(shapes.size()-1); }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
for (Shape shape : shapes)
shape.draw((Graphics2D) g);
}
public void select(Point point) {
for (Shape shape : shapes) {
if (shape.intersects(point)) {
selected = shape;
}
}
}
#Override
public Dimension getPreferredSize() {
if (image == null) {
return super.getPreferredSize();
} else {
int w = image.getWidth();
int h = image.getHeight();
return new Dimension(w, h);
}
}
public void pointerDown(Point point) {
mapPanelState.pointerDown(point, this);
}
public void pointerUp(Point point) {
mapPanelState.pointerUp(point, this);
selected = null;
}
public void pointerMoved(Point point, boolean pointerDown) {
mapPanelState.pointerMoved(point, pointerDown, this);
}
#Override
public void run() {
}
}
I want a method that updates the "Shapes" array in a separate thread to stop everything from freezing.
Any suggestions?
Making your JPanel implement Runnable is not the best solution. There is no reason to expose a run() method to other classes.
Instead, create a private void method that takes no arguments. A method reference that refers to that method can act as a Runnable, since it will have the same arguments and return type. You can then pass it to a Thread constructor.
public MapPanel() {
// ...
readImage();
new Thread(this::readShapes, "Reading shapes").start();
// ...
}
private void readShapes() {
try {
List<Shape> newShapes = new ArrayList<>();
URL server = new URL("https://example.com/indonesia/data");
try (InputStream dataSource = server.openStream()) {
while ( /* ... */ ) {
Shape shape = /* ... */;
newShapes.add(shape);
}
}
EventQueue.invokeLater(() -> setShapes(newShapes));
} catch (IOException e) {
e.printStackTrace();
EventQueue.invokeLater(() -> {
JOptionPane.showMessageDialog(getTopLevelContainer(),
"Unable to retrieve data:\n" + e, "Load Error",
JOptionPane.ERROR_MESSAGE);
});
}
}
Notice that calls to methods involving Swing objects are always wrapped in a call to EventQueue.invokeLater, to make sure they run on the correct thread.
It is possible to improve this by creating a progress dialog that shows while the data is being loaded, but that would make this answer much longer and would require more knowledge about the Indonesian API you’re calling.
I'm trying to remove objects that are outside of the JPanel.
However, when I do that I get this error
and my program crashes.
I was told by my lecturer that it's because two Threads are accessing the ArrayList that stores my objects.
I did to synchronize the functions but it didn't work.
Bullet
public void move(){
if(y< -height){
synchronized (this) {
bullet.remove(this);
}
}
y-=5;
}
Relevant Classes:
Application
import javax.swing.*;
public class Application {
public static String path ="C:\\Users\\jarek\\OneDrive\\NUIG Private\\(2) Semester 2 2019\\Next Generation Technologies II CT255\\Assignment 3\\";
private Application(){
JFrame frame = new JFrame("Ihsan The Defender");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GamePanel gamePanel= new GamePanel();
frame.add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
new Thread(gamePanel).start();
}
public static void main (String args[]){
new Application();
}
}
GamePanel
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.security.Key;
import java.util.ArrayList;
public class GamePanel extends JPanel implements Runnable, KeyListener{
private String path = Application.path;
Image gameOverImg = new ImageIcon(path+"//images//gameover1.png").getImage();
private Ihsan ihsan;
private ArrayList <David> david = new ArrayList<>();
private int enemies=5;
private boolean pause=false;
private boolean gameOver=false;
GamePanel(){
ihsan = new Ihsan(this);
for(int i=0; i<enemies; i++){
david.add(new David(this));
}
setFocusable(true);
requestFocusInWindow();
addKeyListener(this);
}
#Override
public void run() {
while (!pause){
repaint();
for(David david:david){
david.move();
}
for(Bullet bullet:Bullet.bullet){
bullet.move();
}
try{Thread.sleep(30);}
catch (InterruptedException e){}
}
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GRAY);
g2d.fillRect(0,0 ,getWidth(), getHeight());
for(David david : david){
g2d.drawImage(david.getImg(), david.getX(), david.getY(), null);
}
g2d.drawImage(ihsan.getImg(), ihsan.getX(), ihsan.getY(), null);
for (Bullet bullet:Bullet.bullet){
g2d.drawImage(bullet.getImg(), bullet.getX(), bullet.getY(), null);
}
if(gameOver){
g2d.drawImage(gameOverImg,0,getHeight()/4,null);
}
}
private static final Dimension DESIRED_SIZE = new Dimension(600,700);
#Override
public Dimension getPreferredSize(){
return DESIRED_SIZE;
}
public void setGameOver(boolean gameOver) {
this.gameOver = gameOver;
}
#Override
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if (key==KeyEvent.VK_D || key==KeyEvent.VK_RIGHT){
ihsan.move(4,0);
System.out.println("Right Key");
}
if (key==KeyEvent.VK_A || key== KeyEvent.VK_LEFT){
ihsan.move(-4,0);
System.out.println("Left Key");
}
if(key==KeyEvent.VK_SPACE){
Bullet.bullet.add(new Bullet(this,ihsan.getX()+(ihsan.getWidth()/2), ihsan.getY()));
}
}
#Override
public void keyTyped(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
public boolean getGameOver(){
return gameOver;
}
}
Bullet
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Bullet {
//Environment
public static ArrayList<Bullet> bullet = new ArrayList<>();
private String path = Application.path;
private GamePanel gp;
//properties
private int x,y;
private int width,height;
private int yVector;
private Image image;
Bullet(GamePanel gp, int x, int y){
image = new ImageIcon(path+"\\images\\javaicon.png").getImage();
width=image.getWidth(null);
height=image.getHeight(null);
this.gp=gp;
this.x=x;
this.y=y;
yVector=5;
}
public void move(){
if(y< -height){
bullet.remove(this);
}
y-=5;
}
public Image getImg(){
return image;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
Your current problem is not with synchronization, but that you modify the bullet list while iterating over it:
// GamePanel.java#run():
for (Bullet bullet:Bullet.bullet) { //your code is iterating over Bullet.bullet here
bullet.move(); //you call Bullet#move here
}
// Bullet.java#move():
public void move(){
if(y< -height){
bullet.remove(this); //this will remove the current bullet from Bullet.bullet
// ultimately causing the ConcurrrentModificationException in GamePanel.run()
}
y-=5;
}
Synchronization won't help, since both actions occur within the same thread.
To solve this issue the Bullet.move() method needs to return a boolean indicating whether it should be removed from the list. And GamePanel.run() must not use an enhanced for loop but an iterator (removing an element from a list using Iterator.remove() is safe if this is the only active Iterator):
// Bullet.java#move():
public boolean move(){
if(y< -height){
return true; // instruct GamePanel.run() to remove this bullet
}
y-=5;
return false; // keep this bullet
}
// GamePanel.java#run():
Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
Bullet bullet = it.next();
if (bullet.move()) { // if bullet should be removed
it.remove(); // remove it from the list
}
}
There are other issues too:
you call #repaint() from your own thread instead of the Swing EDT
repainting iterates over the same Bullet.bullet list without synchronization (which may lead to a ConcurrentModificationException within GamePanel.paint())
The synchronized block must be around every piece of code that accessed or modifies the ArrayList. The object put in the parenthesis must be the same: It’s the lock.
Create a field of type Object named bulletLock for example, and use it as a lock, every time you access bullet.
The error occurs because you’re removing a bullet while another thread is in a for-loop on the list. As there is a concurrent modification, it can’t continue safely.
Another solution would be to make a copy of the ArrayList before your for-loop.
A simple solution to the problem described in Thomas Kläger answer could be:
for(Bullet bullet: new ArrayList(Bullet.bullet) ){ //iterate over a copy
bullet.move();
}
Alternatively:
Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
Bullet bullet = it.next();
bullet.move();
}
without changing other parts of the code.
Please have a look at the following code
First, Please note I am a 100% newbie to Java Mobile.
In here, I am making the light on and vibrate on when user click the button. However, I really wanted to create a SOS application which turn the whole screen into white, and go to black, like that, in the thread. I guess I didn't achieve that by this app because even the lights are on, the buttons are still there. I tried to turn the "Form" color to "white" but it seems like JME has no "Color" class.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Midlet extends MIDlet{
private Form f;
private Display d;
private Command start,stop;
private Thread t;
public Midlet()
{
t = new Thread(new TurnLightOn());
}
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
d.setCurrent(f);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional)
{
this.notifyDestroyed();
}
private class Action implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
f.append("Light is Turnning On");
t.start();
}
}
private class ActionOff implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
}
}
private class TurnLightOn implements Runnable
{
public void run()
{
f.append("Working");
for(int i=0;i<100;i++)
{
try
{
d.flashBacklight(200);
d.vibrate(200);
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
}
Use the javax.microedition.lcdui.Canvas instead of Form. This example can get you started
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
myCanvas = new MyCanvas();
d.setCurrent(myCanvas);
myCanvas.repaint();
}
Now create a canvas and implement paint method like this:
class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center
// clear the screen first
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xffffff); // make sure it is white color
// draw the square, <b>changed to rely on instance variables</b>
<b>g.fillRect(x, y, getWidth(), getHeight());</b>
}
}
For some reason when i run this program instead of outputting one card choice I get 3, sometimes four.
I'm trying to pick a name from the array list include it in the path, then output the image, you can see the result with the system out.
Heres the code:
public class Main {
public static void main (String[] args) {
Main.createScreen();
}
public static void createScreen() {
JFrame p = new JFrame("Angora Realms");
p.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GameGUI g = new GameGUI();
p.add(g);
p.setLocationRelativeTo(null);
p.pack();
p.setVisible(true);
}
}
Here's where i create the GUI and also the paint:
#SuppressWarnings("serial")
public class GameGUI extends JPanel implements ActionListener {
public Button drawCard = new Button("Draw Card");
public GameGUI() {
drawCard.addActionListener(this);
add(drawCard);
}
#Override
public void actionPerformed(ActionEvent event) {
Object cause = event.getSource();
if (cause == drawCard) {
System.out.println("Ay");
repaint();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Cards c = new Cards();
g.drawImage(c.getImage(), 0, 0, 450, 700, this);
}
}
And here is where i choose what card to load:
public class Cards {
static Random random = new Random();
public static String getCard() {
String card = null;
String[] possibleCards = new String[] {"Cheetah", "Lion"};
card = possibleCards[random.nextInt(2)];
System.out.println(card);
return card;
}
public Image getImage() {
Image img = null;
try {
img = ImageIO.read(getClass().getResource("/dev/angora/images/plains/" + Cards.getCard() + ".png"));
}
catch (IOException e) {
e.printStackTrace();
}
return img;
}
}
When I run the code I get 4 system print outs of a random variation of Cheetah and Lion. I've been told before that I'm actually creating 3 instances of my code somewhere, but I have no idea where...
You don't entirely get to decide how and when paintComponent() gets executed, and that usually doesn't matter, because all the method is supposed to do is to paint the component. It gets called when you do a repaint(), but it also gets called when Swing thinks the UI needs to get updated (which can be when the window changes focus, it gets resized or a bunch of other reasons).
However, you've given it some responsibility that it shouldn't have—to instantiate Cards.
Move Cards c = new Cards(); from paintComponent() into actionPerformed(ActionEvent event) where it belongs and you should be fine.
how else could i input a variable into the graphics method?
In general, you have a setter method and then you save the data as an instance variable in your class that the painting method can reference.. This is how methods like setBackground(...), setForeground(...), setFont(...) work.
So maybe you have a method like drawCard(...) in your class that will get a random card and set your "image" instance variable. Then you invoke repaint() in the drawCard() method so the component can repaint itself.
The component, not the application should be responsible for painting itself when a property of the component changes.
I'm learning Java, and now that I'm over the packages hump, things are going smoothly. I can draw similarities between most things I'm learning with things I already know at least the concept of. But what on earth is going on with the following bit of code? Is it some form of constructor, or anonymous object?
Something obj = new Something()
{
private static final int num = 3;
public void meth()
{
// w/e
}
};
You got it - this creates an anonymous inner class of Something.
See also: Nested Classes (The Java Tutorial) and Anonymous Classes.
/**
* Notice there's only one thing in this that isn't defined:
* It still needs public abstract void triggerEvent();
*/
public abstract static class TopButton extends JPanel implements MouseListener {
protected ButtonPanel parent;
private String text;
public TopButton(ButtonPanel bp, String text) { parent = bp; this.text = text; addMouseListener(this); }
public void mouseClicked(MouseEvent e) { triggerEvent(); }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public abstract void triggerEvent();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Color oldColor = g.getColor();
Font oldFont = g.getFont();
Font newFont = new Font(oldFont.getName(),oldFont.getStyle(),oldFont.getSize());
g.setFont(newFont);
g.setColor(Color.black);
g.drawString(text, 20, 20);
g.setFont(oldFont);
g.setColor(oldColor);
}
}
Now, when I actually define my buttons, I do this. By providing the one line it needs, the only thing that makes it different from others. Now I could make a new file for each one, and define a new class for each one. This is much simpler.
private static void loadButtonPanelButtons() {
/* This button should tell the parent to bring up the save screen */
TopButton save = new TopButton(buttonPanel,"Save") {
public void triggerEvent() { parent.triggerSave(); }
};
save.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(save);
/* This button should tell the parent to bring up the load screen */
TopButton load = new TopButton(buttonPanel,"Load") {
public void triggerEvent() { parent.triggerLoad(); }
};
load.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(load);
TopButton addTile = new TopButton(buttonPanel,"Add Tile") {
public void triggerEvent() { parent.triggerAddTile(); }
};
addTile.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(addTile);
TopButton saveTiles = new TopButton(buttonPanel,"Save Tiles") {
public void triggerEvent() { parent.triggerStyleSave(); }
};
saveTiles.setBorder(LineBorder.createBlackLineBorder());
buttonPanel.add(saveTiles);
}
Now, when I handle the buttons being pressed, remember back in the definition of TopButton... there was
public void mouseClicked(MouseEvent e) { triggerEvent(); }
We know triggerEvent() eventually gets defined. We can define it on a per-button basis, and when the panel gets clicked, no matter what we defined triggerEvent() to be, it gets called.
Such construct creates an anonymous inner class of a class where this construct is executed, and derived from Something (not an inner class of Something).
The idea is to quickly provide implementations for abstract classes, interfaces, or override some functionality of a class.
(new Thread(){ public void run() { System.out.println("executed on another thread"); }}).start();