Cannot remove shapes in Java using mouseEvents - java

I am trying to make an original game in Eclipse and I am having some trouble writing code that will remove GObjects and change the properties of others, using the mouseClicked method.
The problem seems to be that the private instant variables are not being recognised in the mousePressed method.
Can someone please advise on what I'm getting wrong here? I have spent a whole day on this and some help will be greatly appreciated.
Kind regards,
Mehul
*/*
* File: Linesv1.java
* 07/08/2012
*
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.MenuEvent;
public class Linesv1 extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int BACKGROUND_WIDTH = 400;
public static final int BACKGROUND_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = BACKGROUND_WIDTH;
private static final int HEIGHT = BACKGROUND_HEIGHT;
/** Dimensions of triangle*/
private static final int LENGTH_OF_TRIANGLE_SIDE = 100;
/** Dimensions of arc height*/
private static final int ARC_HEIGHT = 100;
/** Dimensions of radius of switches*/
private static final int SWITCH_RADII = 5;
// private instant variables
private GObject switchA;
private GOval switchB;
private GObject triangle;
private GObject bottomArc;
public void run() {
addMouseListeners();
setUpGame();
}
public void setUpGame(){
//add central triangle
GPolygon triangle = new GPolygon (WIDTH/2,HEIGHT/2);
triangle.addVertex(0,-LENGTH_OF_TRIANGLE_SIDE*2/3);
triangle.addVertex(LENGTH_OF_TRIANGLE_SIDE/2,+LENGTH_OF_TRIANGLE_SIDE*1/3);
triangle.addVertex(-LENGTH_OF_TRIANGLE_SIDE/2,+LENGTH_OF_TRIANGLE_SIDE*1/3);
triangle.setFilled(true);
triangle.setFillColor(Color.green);
add(triangle);
//add topArc
GArc bottomArc = new GArc (WIDTH/2-LENGTH_OF_TRIANGLE_SIDE/2, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-ARC_HEIGHT/2, ARC_HEIGHT,ARC_HEIGHT,0,-180);
bottomArc.setFilled(true);
bottomArc.setFillColor(Color.green);
add(bottomArc);
//add switches to the bottom of the triangle
GOval switchA = new GOval (WIDTH/2-LENGTH_OF_TRIANGLE_SIDE/2-SWITCH_RADII, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-SWITCH_RADII,SWITCH_RADII*2,SWITCH_RADII*2);
switchA.setFilled(true);
switchA.setFillColor(Color.black);
add(switchA);
//add switches to the bottom of the triangle
GOval switchB = new GOval (WIDTH/2+LENGTH_OF_TRIANGLE_SIDE/2-SWITCH_RADII, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-SWITCH_RADII,SWITCH_RADII*2,SWITCH_RADII*2);
switchB.setFilled(true);
switchB.setFillColor(Color.black);
add(switchB);
}
public void mousePressed(MouseEvent e) {
findObject(e.getX(),e.getY());
GObject check = findObject(e.getX(),e.getY());
if (check!=null){
remove(triangle);
switchA.setColor(Color.cyan);
}
}
private GObject findObject(int a, int b){
if(getElementAt(a,b) != null) {
return getElementAt(a,b);
} else {
return null;
}
}
}*

The triangle you are trying to remove is not the one you added. You created a local GPolygon called triangle in setupGame() and added that, not the private member.
private GObject triangle; // this is uninitialized
public void setUpGame(){
GPolygon triangle = new GPolygon (WIDTH/2,HEIGHT/2); // this is hiding your member variable
// THIS IS LOCAL COPY, NOT MEMBER VARIABLE
add(triangle);
And then later you try to remove the uninitialized member variable, so nothing happens. You probably don't want a separate local copy of triangle. You probably want
public void setUpGame() {
triangle = new GPolygon (WIDTH/2, HEIGHT/2);
// ...
add(triangle); // Now, you are adding the member
}

Related

Java Swing DrawRect: creating new replaces old dimensions

I tried making two rectangles using the class below: DrawRect but when I create a new DrawRect object it replaces old one's width and height.
package MemDiagramVisualizer;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
public class DrawRect extends JPanel {
private static int RECT_X;
private static int RECT_Y;
private static int RECT_WIDTH;
private static int RECT_HEIGHT;
public DrawRect(int w, int h) {
RECT_X = 20;
RECT_Y = 20;
RECT_WIDTH = w;
RECT_HEIGHT = h;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RECT_WIDTH + 2 * RECT_X, RECT_HEIGHT + 2 * RECT_Y);
}
}
JPanel visDisplay = new JPanel();
visDisplay.setLayout(new GridLayout(1,3));
DrawRect rec2 = new DrawRect(40,60);
visDisplay.add(rec2);
DrawRect rec = new DrawRect(100,600);
visDisplay.add(rec);
The code above when added to frame content pane creates two rectangles with 100,600 dimensions
You have a couple of issues in your program:
All these 4 variables:
private static int RECT_X;
private static int RECT_Y;
private static int RECT_WIDTH;
private static int RECT_HEIGHT;
They all are static, but you're trying to change them inside the program, this will apply for all the instances of your program and they all are gonna share the value. In this case I'd suggest you to remove that and you should be good. This is the reason why when you create another class with the same code, it worked.
RECT_X = 20; and RECT_Y = 20; inside the constructor, if these are constants, then initialize them at the top and don't set them in every instance of the class.
Not an error, but depending on your requirements, you might want to stop using multiple JPanels and instead use the Shape API as shown in this answer to create an Array of Shapes that you can draw in a single JPanel. Again, this all depends on your needs.
After removing the static keyword from those constants above, we have this:

Creating a java method that can continuously update variables

I've been taking AP Computer Science this year as a sophomore in high school and we mainly cover material like loops, classes, methods, general CS logic, and some math stuff. I am missing what I really loved about coding in the first place, making games. Now every game I have made had some sort of way to manage it whether it was using timers in visual basic or a XNA plugin for c# that setup a update method for me. The problem is I have not learned how to do this for java in my course. I've read up a little on threads and implements runnable but i'm not really sure where I'm going with it.
Class 1
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GFXScreen extends JFrame
{
/**
* #param screenHeigth
* #param screenHeigth
* #param The file name of the image. Make sure to include the extension type also
* #param The title at the top of the running screen
* #param The height of the screen
* #param The width of the screen
*/
public GFXScreen(String fileName, String screenTitle, int screenHeight, int screenWidth)
{
setLayout(new FlowLayout());
image1 = new ImageIcon(getClass().getResource(fileName));
label1 = new JLabel(image1);
this.add(label1);
//Set up JFrame
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
this.setTitle(screenTitle);
this.setSize(screenWidth, screenHeight);
}
/**
* #param desired amount to move picture
*/
public void updatePic(int increment)
{
//update pos
label1.setBounds(label1.bounds().x, label1.bounds().y - increment,
label1.bounds().width, label1.bounds().height);
}
private ImageIcon image1;
private JLabel label1;
}
Class 2
public class MainClass implements Runnable {
public static void main(String[] args)
{
(new Thread(new MainClass())).start();
GFXScreen gfx = new GFXScreen("pixel_man.png", "pixel_Man", 1000, 1000);
}
public void run()
{
gfx.updatePic(1);
}
}
In this instance what I want to happen is, I want a picture that starts in the top to slowly move down smoothly to the bottom. How would i do this?
Suggestions:
Again, a Swing Timer works well for simple Swing animations or simple game loops. It may not be the greatest choice for complex or rigorous tame loops as its timing is not precise.
Most game loops will not be absolutely precise with time slices
And so your game model should take this into consideration and should note absolute time slices and use that information in its physics engine or animation.
If you must use background threading, do take care that most all Swing calls are made on the Swing event thread. To do otherwise will invite pernicious infrequent and difficult to debug program-ending exceptions. For more details on this, please read Concurrency in Swing.
I avoid using null layouts, except when animating components, as this will allow my animation engine to place the component absolutely.
When posting code here for us to test, it's best to avoid code that uses local images. Either have the code use an image easily available to all as a URL or create your own image in your code (see below for a simple example).
Your compiler should be complaining to you about your using deprecated methods, such as bounds(...), and more importantly, you should heed those complaints as they're there for a reason and suggest increased risk and danger if you use them. So don't use those methods, but instead check the Java API for better substitutes.
Just my own personal pet peeve -- please indicate that you've at least read our comments. No one likes putting effort and consideration into trying to help, only to be ignored. I almost didn't post this answer because of this.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class GfxPanel extends JPanel {
private static final int BI_WIDTH = 26;
private static final int BI_HEIGHT = BI_WIDTH;
private static final int GAP = 6;
private static final Point INITIAL_LOCATION = new Point(0, 0);
private static final int TIMER_DELAY = 40;
public static final int STEP = 1;
private ImageIcon image1;
private JLabel label1;
private Point labelLocation = INITIAL_LOCATION;
private int prefW;
private int prefH;
private Timer timer;
public GfxPanel(int width, int height) {
// the only time I use null layouts is for component animation.
setLayout(null);
this.prefW = width;
this.prefH = height;
// My program creates its image so you can run it without an image file
image1 = new ImageIcon(createMyImage());
label1 = new JLabel(image1);
label1.setSize(label1.getPreferredSize());
label1.setLocation(labelLocation);
this.add(label1);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
public void startAnimation() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
labelLocation = INITIAL_LOCATION;
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
// My program creates its image so you can run it without an image file
private Image createMyImage() {
BufferedImage bi = new BufferedImage(BI_WIDTH, BI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(Color.red);
g2.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.setColor(Color.blue);
int x = GAP;
int y = x;
int width = BI_WIDTH - 2 * GAP;
int height = BI_HEIGHT - 2 * GAP;
g2.fillRect(x, y, width, height);
g2.dispose();
return bi;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = labelLocation.x + STEP;
int y = labelLocation.y + STEP;
labelLocation = new Point(x, y);
label1.setLocation(labelLocation);
repaint();
if (x + BI_WIDTH > getWidth() || y + BI_HEIGHT > getHeight()) {
System.out.println("Stopping Timer");
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
final GfxPanel gfxPanel = new GfxPanel(900, 750);
JButton button = new JButton(new AbstractAction("Animate") {
#Override
public void actionPerformed(ActionEvent arg0) {
gfxPanel.startAnimation();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
JFrame frame = new JFrame("GFXScreen");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(gfxPanel);
frame.getContentPane().add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
What I always use is an infinite loop that calls an update method each iteration, in that method, you would do whatever was required to update the state of the game or render a GUI.
Example
public static void main(String[] args){
// Initialise game
while(true){
updateGame();
}
}
public static void updateGame(){
// Update things here.
}
What I also do ,which is a little more complex, is create and interface called IUpdateListener and have certain classes that are specialised for a certain element of the game. I would example have an InputListener, an AIListener, each handling a certain element of game updating.
public interface IUpdateListener{
public void update();
}
public class Main{
public static ArrayList<IUpdateListener> listeners = new ArrayList<IUpdateListener>();
public static void main(String[] args){
listeners.add(new InputListener());
while(true){
for(IUpdateListener listener : listeners){
listener.update();
}
}
}
}
public class InputListener implements IUpdateListener{
public void update(){
// Handle user input here etc
}
}

How to share same GCanvas in a GraphicsProgram in java?

I'm new at the Java world, and I started to take the Stanford cs106A course. I made the "breakout" problem which consists of making a simple breakout game.
I want to go even further, and my point is to create new Classes that can incorporate objects on the GCanvas of the main class.
So, now I have something like this:
public class Breakout extends GraphicsProgram {
//...
public void run();
private void playGame();
private void checkForPaddleCollisions();
private boolean passedBorderline();
private boolean moreBricksRemainig();
private void checkForCollisions();
private void updatePuntuation(GObject obj);
private void changeDirection(int temp);
private GObject getCollidingObject(double x, double y);
private void moveBall();
private void checkForBounderies();
private boolean hitsLeftWall();
private boolean hitsRightWall();
private boolean hitsRoof();
private void setRandomVx();
private void setBall();
private void setUp();
private void setLabels();
private void setPaddle();
public void mouseDragged (MouseEvent e);
public void mousePressed(MouseEvent i);
private void placeBricks();
private GOval ball;
private GRect paddle;
private GLabel win, lose, lifes, puntuationLabel;
private GPoint last;
private double vx, vy = 3.0;
private RandomGenerator rgen = RandomGenerator.getInstance();
private int Delay = 50;
private int counterBricks = NBRICKS_PER_ROW * NBRICK_ROWS;
private AudioClip bounceClip = MediaTools.loadAudioClip("bounce.au");
private int noPaddleBugs = 0;
private int puntuation = 0;
private int actualPunt = 0;
}
This is the main class of the game. What I want to do is to create a new class named PowerUps. This class can put any object on the GCanvas object of the main class at any point of the game. I tried to access the GCanvas of the main class by saying something like Breakout."canvasproperty".add(newObject), but it seems like it's not permitted.
Then I thought: I can create a class that extends GCanvas and then initialize a public property in the main class of that "new" canvas. With this method, I am able to insert new objects in this canvas from outer classes, but the problem is that when I run the Breakout class, the "new" canvas property doesn't show...
I don't know if I expressed myself well, but I tried, if anyone can help me, i would be very grateful?
Thanks in advance.

How to handle window size for GraphicsProgram (ACM library)?

I want to define the size of the window, but I did not find a clean way to do it. SetSize() gives a strange result:
public class Test extends GraphicsProgram {
public void run() {
setSize(400, 600);
add(new GLabel("Width: " + getWidth(), 30, 30));
add(new GLabel("Height: " + getHeight(), 30, 50));
}
}
The result is 384 x 542. The gap is always the same (-16 x -58), so it's easy to build a work around. Is there a way to define the size in useful pixels directly?
I found a solution to this while studying the code of a Stanford course project (breakout). GraphicsProgram is not constructed according to its fields, WIDTH, HEIGHT, EAST, CENTER etc, automatically. Therefore we need a resize() to set the window size which acm.program.GraphicsProgram inherits from java.applet.Applet.
Simply adding a resize() and pause() will do the work.
public class Test extends GraphicsProgram {
private static final int WIDTH = 400;
private static final int HEIGHT = 600;
private static final int PAUSE = 10; // or whatever interval you like
public void run() {
this.resize(WIDTH,HEIGHT);
pause(PAUSE);
// game logic here
...
}
}
The pause() is necessary as the resize takes time. It avoids the mis-displacement of components if you are adding them right after resizing.
public void init() {
setSize(400, 600);
}
instead of setting the size in the run method do it on the init!
After class:
public static final int APPLICATION_WIDTH = 900; // x size of window
Before run:
public static final int APPLICATION_HEIGHT = 540; // y size of window
No need for setSize or resize.
You just need to add this two public variables:
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
and these two if you need exactly WIDTH and HEIGHT to use:
/** Dimensions of window (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;

Creating a moving object when user presses a key (Java)

The idea of my program is to create a picture and have that picture move up in a graphical window, which is exactly what the rollBall() method does. The method works when I put the rollBall() method in the run() method. But the issue lies it cannot run when I put the rollBall() method inside the keyPressed() method.
I am using the acm.jar library as it is a useful tool for creating java graphical program much easier.
Could someone please point me in the right direction.
This is my code...
import java.awt.Color;
import java.awt.event.KeyEvent;
import acm.graphics.GImage;
import acm.graphics.GOval;
import acm.program.GraphicsProgram;
import acm.util.RandomGenerator;
public class BallDrop extends GraphicsProgram {
/** width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 900;
public static final int APPLICATION_HEIGHT = 768;
private static final double GRAVITY = 1;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 50;
private static final int WIDTH = APPLICATION_WIDTH;
public void run() {
setSize(APPLICATION_WIDTH, APPLICATION_HEIGHT);
addKeyListeners();
}
public void keyPressed(KeyEvent e){
char linkMoveRightKey = e.getKeyChar();
if(linkMoveRightKey == 'z'){
rollBall();
}
}
private void rollBall(){
setup_Ball();
game_Loop();
}
private void setup_Ball(){
pic = new GImage("link.png");
add(pic,gameBallInitialLocationX, gameBallInitialLocationY);
}
private void game_Loop(){
while(pic.getX() > 0 ){
move_Ball();
pause(DELAY);
}
}
private void move_Ball() {
ballVelocityX = 0;
ballVelocityY -= GRAVITY;
pic.move(ballVelocityX, ballVelocityY);
}
private RandomGenerator rgen = RandomGenerator.getInstance();
private GImage pic;
private int gameBallInitialLocationX = 500;
private int gameBallInitialLocationY = 500;
private int ballVelocityX = (int) rgen.nextDouble(3.0, 5.0);
private int ballVelocityY =10;
private static final int DELAY = 50;
}
I just read the manual and it is my understanding that you are calling the wrong method:
Instead of calling run() method, define the init() method.
Also the setup_Ball() should be inside init() and not inside rollBall() - You only want to initialize the ball when the program starts and not when every time the key is pressed.
So instead of run() define init() and also remove setup_Ball() from rollBall() method :
public void init() {
setSize(APPLICATION_WIDTH, APPLICATION_HEIGHT);
setup_Ball();
addKeyListeners();
}
Note: You can use the run() method when you want some animation to appear when the program starts without waiting for key to be pressed. In that case, you can call the appropriate methods in run()

Categories