I am learning something new and somewhat difficult in Java-which is graphics! Below I explain what the two classes do. My new obstacle right now is coming up with a way to draw a different image for (ie: a projectile like a laser) coming from the ball by only pressing Z.
The problem is if I write a method for example: "g.drawImage(laser,laser_dx,laser_dy,this) in the if statement that contains "KeyEvent.VK_Z", my keyPressed method all of sudden says "this method is not used locally". What are my approaches to solving such an obstacle?
What I've done so far is written a nested class inside the "GameBoard" class that contains all the keyboard events of my program.
private class Adapter extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_RIGHT)
{
ball_dx += ball_velocity;
}
if(keyCode == KeyEvent.VK_LEFT)
{
ball_dx -= ball_velocity;
}
if(keyCode == KeyEvent.VK_Z){
}
}
}
Here's the drawing graphics method in a separate class called "Gameboard": This class just draws the image of a green ball(which is a .png image) and it can move left and right with the arrow keys!
public class GameBoard extends JPanel implements ActionListener
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(ball, ball_dx, ball_dy, this);
Toolkit.getDefaultToolkit().sync();
}
public void actionPerformed(ActionEvent arg0)
{
repaint();
}
}
You have to rethink the logic: the code that handles the key events and the code that draws everything should share a state so that
events set the state
drawings change according to the state
Just to make it simple and to give you the idea:
boolean isLaser = false;
public void keyPressed(KeyEvent e) {
isLaser = true;
}
public void keyReleased(KeyEvent e) {
isLaser = false;
}
public void paintComponent(Graphics g) {
if (isLaser)
// do something
}
Of course in a more complex environment you would have a more structured solution like
List<Entity> entities = new ArrayList<Entity>();
public void paintComponent(Graphics g) {
for (Entity e : entities)
e.draw(g);
}
public void keyPressed() {
entities.add(new LaserEntity(...));
}
Related
I am trying to make Pong. I sent this program to a friend and when he ran it the first time he got my bug but later it ran fine. I always seem to get the bug. My paddle can move up and down. It seems to move correctly and it gets drawn in the right place but it always flickers to the start position and back again. It looks like it gets drawn once at the start position and then at the right positions, flickering between the 2 positions forever.
NetBeans complains about this.addKeyListener(this);. It says "Leaking this in constructor". Here is my code: (sorry in advance, first time posting)
I exported it from netbeans if anyone wants to take a look:
http://s000.tinyupload.com/?file_id=00856291054786890080
public class LeikGluggi extends javax.swing.JPanel implements ActionListener, KeyListener {
Spilari1 spilari1 = new Spilari1();//make paddle
Bolti bolti = new Bolti(); //make ball
public LeikGluggi() {
initComponents();
setSize(Leikbord.GLUGGI_BREIDD,Leikbord.GLUGGI_HAED);
this.addKeyListener(this);
this.setFocusable(true);
int i = 0;
Timer tim = new Timer(50, this);
tim.start();
}
private void uppfaera()//update
{
spilari1.uppfaera();//paddle update
bolti.uppfaera(); // ball update
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);//
g.setColor(Color.WHITE);
g.fillRect(0,0,Leikbord.GLUGGI_BREIDD,Leikbord.GLUGGI_HAED); //background
g.setColor(Color.BLUE);
spilari1.paint(g); //paddle drawn
bolti.paint(g); //ball drawn
}
#Override
public void actionPerformed(ActionEvent e)
{
uppfaera();//update called
repaint();
}
#Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_UP)
{
spilari1.setyHradi(-4);
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
spilari1.setyHradi(4);
}
}
#Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_UP)
{
spilari1.setyHradi(0);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
spilari1.setyHradi(0);
}
}
public void keyTyped(KeyEvent e)
{
}
And the code for my paddle
package is.hi.vidmotsforritun.pong2.teikning;
import java.awt.Color;
import java.awt.Graphics;
public class Spilari1 {
private final int breidd = 10 ;
private final int lengd = 75;
private int y = (Leikbord.GLUGGI_HAED/2)-lengd/2;
private int yHradi = 0;
public Spilari1()
{
}
public void uppfaera()
{
y = y + yHradi;
System.out.println("HRAÐI ER "+yHradi);
}
public void paint(Graphics g)
{
g.setColor(Color.BLUE);
g.fillRect(25, y, breidd, lengd);
System.out.println("Y er "+y);
}
public void setyHradi(int hradi)
{
yHradi = hradi;
}
}
According to this Thread ("http://forums.netbeans.org/post-134877.html") the problem is that you pass a reference of this to an outside class while not fully initialized (in constructor). So you need to do this later in an initialization method
I'm creating a java game. In the game there are a hero and a bubble. The hero is supposed to move when I press the arrow keys and the bubble is supposed to have continuous diagonal movement. When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both I just get a very small square! I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working. Probably I have to define some type of layout to the JPanels.
What am I doing wrong?
Code:
public class Pang {
public static void main(String[] args) {
JFrame f=new JFrame();
JPanel gamePanel=new JPanel();
gamePanel.setPreferredSize(new Dimension(800, 600));
DrawHero d=new DrawHero();
DrawBubble bubble=new DrawBubble();
gamePanel.add(d);
gamePanel.add(bubble);
f.add(gamePanel);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800, 600);
}
}
public class DrawHero extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 0, y = 0, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawHero() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
imageHero = getHeroImage();
g2.drawImage(imageHero, x, y, 40, 40, null);
}
public Image getHeroImage() {
Image image = null;
image = getImage("hero.png");
return image;
}
public Image getImage(String path) {
Image tempImage = null;
try {
URL heroiURL = DrawHero.class.getResource(path);
tempImage = Toolkit.getDefaultToolkit().getImage(heroiURL);
} catch (Exception e) {
System.out.println("Error loading hero image! - "
+ e.getMessage());
}
return tempImage;
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void moveUp() {
y = y - step;
}
public void moveDown() {
y = y + step;
}
public void moveLeft() {
x = x - step;
}
public void moveRight() {
x = x + step;
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) {
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN) {
moveDown();
}
if (keyCode == KeyEvent.VK_LEFT) {
moveLeft();
}
if (keyCode == KeyEvent.VK_RIGHT) {
moveRight();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
public class DrawBubble extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 100, y = 200, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawBubble() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fill(new Ellipse2D.Double(x, y, 40, 40));
}
public void actionPerformed(ActionEvent e) {
x=x+dx;
y=y+dy;
repaint();
}
public void moveBubble() {
dy=2;
dx=2;
}
public void keyPressed(KeyEvent e) {
moveBubble();
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
I recommend that neither the DrawHero nor DrawBubble (which should be called Hero nor Bubble respectively) should extend any JComponent. Instead each should simply know how to draw itself to a Graphics object passed to it, when requested to do so.
Then a single GameField or PlayingArea class should keep references to all the Bubble objects and the Hero and draw call the draw(Graphics) method of those objects.
Using this approach it is not necessary to worry about layouts within the GameField component (they become irrelevant).
That is the basic strategy I pursue for rendering the stationary objects in this answer to [Collision detection with complex shapes.
When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both i just get a very small square!
The default layout manager for a JFrame is a BorderLayout. When you use add(component) without a constraint the component goes to the CENTER. Only one component can be added to the CENTER, so only the last one added is displayed.
I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working.
The default layout manager for a JPanel is the FlowLayout which respects the preferred size of component. The problem is you don't override the getPreferredSize() method so the size is (0, 0) and there is nothing to paint.
Probably I have to define some type of layout to the JPanels.
Actually since you want random motion you need to use a null layout on the panel and then use the setSize() and setLocation() method of your components to position the components.
Then when you do this the custom painting should always be done at (0, 0) instead of (x, y) since the location will control where the component is painted on the panel.
So I am working on a project for a class. And I've been struggling with this issue for a while now. The code below is a start screen, and when the enter key is pressed (when atTitle turns to false) I would like it to draw the next image. The problem with that is I can not think of a way for it to draw the next image when it turns to false. I've tried using ifs and whiles. Mainly the problem is that you obviously put another public void paintComponent in an if statement. And I can't carry the Graphics g variable to the KeyPressed method.
I'm stuck.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
AffineTransform at = new AffineTransform();
g2.setTransform(at);
if (atTitle == true) {
g.drawImage(titlescreen, 0, 0, this);
if (start_visible == true) {
g.drawImage(start_symbol, -70, 30, this);
jf.addKeyListener(this);
}
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
//int keyCode = e.getKeyCode();
if (atTitle == true) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
atTitle = false;
System.out.println("It Works.");
}
}
}
you need to call repaint and wait system calls to paint function. you should not keep graphic object and use it in a function is not child function of paint
as a simple canvas component, you can use this one
class Game extends Canvas implements Runnable {
private Thread thread;
public Game(){
thread = new Thread(this);
thread.start();
}
#Override
public void paint(Graphics g) {
// paint your game
}
public void stop(){
thread = null;
}
#Override
public void run() {
while (thread == Thread.currentThread()){
long ti = System.currentTimeMillis();
repaint();
long ti2 = System.currentTimeMillis();
long waitTime = 60 - (ti2-ti);
if (waitTime > 0){
try {
Thread.sleep(waitTime);
} catch (Exception e){
}
}
}
}
}
If you are using JPanel you should:
panel.setFocusable(true);
panel.requestFocusInWindow();
Referenced here
Also you can call panel.revalidate() and/or panel.repaint() in the key press.
Maybe try to stay away from JPanel and Swing altogether (not suited for gaming, but for forms) and just use the graphics2D functionality with a Window, Frame and Canvas, as suggested in previous comment.
so I am trying to make a simple program where you click on the screen and it creates a block that falls and collides with a larger block beneath and sticks to it. Kind of like a simple collision program. The problem is when I create one block it deletes the block previously. I made an array, but it still does this. Do any of you know what Im doing wrong? Im sure its a simple fix.
public class Screen extends JPanel implements Runnable {
public static JLabel statusbar; //displays a status bar showing what mouse movements are taking place
private Image cat; //image of the cat
public int xCoord ; //get the coordinates of the mouse pressed
public int yCoord ;
public int xCreate;
public int yCreate;
public Rectangle Ground;
public Rectangle Block;
public boolean isClicked = false;
public int clickCount = 0;
Rectangle blocks[] = new Rectangle[10];
int blocknum = 0;
public Screen(Frame frame) {
loadPic(); //calls the loadPic method above
Handlerclass handler = new Handlerclass(); //creates a new class to use the mouse motion listener
System.out.println("mouse works!");
addMouseListener(handler);
addMouseMotionListener(handler);
statusbar = new JLabel("default");
add(statusbar);
}
public void run(){ //this is the game run loop
System.out.println("this is running");
try{
} catch(Exception e) {} //exception handling
}
public void loadPic(){ //loads the picture from the other project but its the same pic
cat = new ImageIcon("C:\\Users\\Camtronius\\Documents\\NetBeansProjects\\Moving Block Proj\\src\\MovingBlock\\catIcon1.png").getImage(); //gets the image
System.out.println("Image Loaded!");
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g); //paints the component, the picture, on top
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(cat, xCoord, yCoord, null);
g2d.setColor(Color.BLUE);
Ground = new Rectangle(0,450,550,50);
g2d.fillRect(0,450, 550, 50);
for(Rectangle blocknum : blocks){
if (blocks != null) {
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
}
}
//move();
}
public void move(){
if(yCreate<400){
yCreate+=1;
}else{
}
if(Ground.intersects(blocks[blocknum])){
yCreate=400;
System.out.println("contains!");
}
}
private class Handlerclass implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent event){
}
public void mousePressed(MouseEvent event){
}
public void mouseReleased(MouseEvent event){
if(blocknum<blocks.length){
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
repaint();
}
blocknum=blocknum+1;
}
public void mouseEntered(MouseEvent event){
}
public void mouseExited(MouseEvent event){
}
public void mouseDragged(MouseEvent event){
}
public void mouseMoved(MouseEvent event){
statusbar.setText(String.format("Coordinates are: %d, %d", event.getX(),event.getY()));
xCoord=event.getX();
yCoord=event.getY();
}
}
}
Painting is a destructive process. That is, when a new paint cycle runs, the previous contents of the Graphics context should be cleared...
So, in you paintComponent method you are only painting the last block...
if(isClicked = true){
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
repaint(); // THIS IS A BAD IDEA
}
DO NOT call any method that might cause repaint to be called. This will put you in a potential cycle of death that will consume your CPU.
Instead, you should loop through the blocks array and paint each one...
for (Rectangle block : blocks) {
if (block != null) {
g2d.setColor(Color.RED);
g2d.fill(block);
}
}
And in you mouseReleased method, you should be adding the new rectangles...
public void mouseReleased(MouseEvent event){
blocknum=blocknum+1;
if (blocknum < blocks.length) {
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(xCreate, yCreate, 50, 50);
}
}
I'd suggest you take a look at Custom Painting, Painting in AWT and Swing and Concurrency in Swing for more details
I am starting out with Java and I have a problem. When people click "a" on the java applet, I want it to draw a yellow rectangle and if they press anything else it draws a black rectangle but nothing happens.
import java.awt.*;
import java.applet.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
public class guitarGame extends Applet implements ActionListener, KeyListener{
Timer timer = new Timer (1000, this);
String s = "";
char a;
int selection;
public void keyReleased(KeyEvent ae){}
public void keyPressed(KeyEvent ae){}
public void keyTyped(KeyEvent ae){
a = ae.getKeyChar();
}
public void init(){
addKeyListener(this);
}
public void actionPerformed (ActionEvent ae)
{
if (a == a)
{
selection = 1;
}
else{
selection = 2;
}
}
public void paint (Graphics g)
{
if (selection == 1){
g.setColor(Color.YELLOW);
g.fillRect(100,100,100,100);
}
if (selection == 2){
g.setColor(Color.YELLOW);
g.fillRect(100,100,100,100);
}
repaint();
}
}
Any help?
You are not doing anything with your KeyListener methods. Your ActionListener will never be called as it hasnt been registered with any component.
public void keyPressed(KeyEvent ae){
char keyChar = ae.getKeyChar();
if (keyChar == 'a' ) {
selection = 1;
} else {
selection = 2;
}
repaint();
}
Some suggestions:
Don't call repaint inside paint - This will cause the latter to loop indefinitely
Use enums rather than magic numbers for paint selection.
Call super.paint(g)
Consider using Swing which has much better performance over the old heavyweight AWT.