Java Key Events and Timer control - java

Im almost finished with this car project im working on but cant seem to get the key events to work. I think it has to do with my action listener with my timer but im not sure. When I press the up arrow key the timer delay is supposed to decrease and vice versa for the down arrow key. I have the commands written but they are not registering input. If anyone could give me some pointers I'd appreciate it
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class RaceCar extends JFrame{
public RaceCar(){
add(new CarPic());
}
public static void main(String[] args){
JFrame frame = new RaceCar();
frame.setTitle("Brady Kedge: Race Car");
frame.setSize(300, 150);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public class CarPic extends JPanel implements KeyListener
{
private int x = 0;
private int y = 150;
private int z = 300;
Timer mytimer = new Timer(50, new ActionListener());
public CarPic()
{
mytimer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
y = getHeight();
z = getWidth();
g.setColor(Color.WHITE);
g.fillRect(0, 0, z, y);
Polygon polygon = new Polygon();
polygon.addPoint(x + 10, y - 20);
polygon.addPoint(x + 20, y - 30);
polygon.addPoint(x + 30, y - 30);
polygon.addPoint(x + 40, y - 20);
if(x < z - 40)
{
g.setColor(Color.BLACK);
g.fillOval(x + 10, y - 10, 10, 10);
g.fillOval(x + 30, y - 10, 10, 10);
g.setColor(Color.BLUE);
g.fillRect(x, y - 20, 50, 10);
g.setColor(Color.BLUE);
g.fillPolygon(polygon);
}
else
x = 0;
}
public void actionPerformed(ActionEvent e){
x+=10;
repaint();
}
#Override
public void keyTyped(KeyEvent k) {
//Fill
}
#Override
public void keyPressed(KeyEvent k) {
int delay = mytimer.getDelay();
if(k.getKeyCode() == KeyEvent.VK_UP)
mytimer.setDelay(delay > 10 ? delay - 10 : 0);
else if(k.getKeyCode() == KeyEvent.VK_DOWN)
mytimer.setDelay(delay < 5000 ? delay + 10 : 5000);
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
}

First of all, you never register a KeyListener with your component (implementing KeyListener isn't enough).
Second of all, KeyListener will only raise KeyEvents if the component it is registered to has focus and is focusable.
A better solution would be to use the key bindings API, which provides you with the means to configure the focus level at which a component will trigger key events.
Also, personally, instead of modifying the Timer delay, I would have use a speed modifier (of type double) which would be percentage of the speed you want. In this way 1 would normal speed, 0.5 half speed and 2 double speed, for example.

Related

How to loop something in my paint function? [duplicate]

This question already has answers here:
Image change on hover Java
(2 answers)
Closed 5 years ago.
So I am trying to set up this thing that changes an image when hovered over. I have already set up the mouseListener to know if the mouse is over my image. I have a variable that has the image location stored in it and it changes when the image is hovered over. When my paint command is run it paints the default image and when I hover over it, it doesn't change because it is not painted again. How can I make it so that it repaints it again when the image location is changed. BTW the mouseListener is in a different class than the image.
My image:
private String settingsConfig = snake.settingsConfig;
settingsImage = new ImageIcon(getClass().getResource(settingsConfig));
settingsImage.paintIcon(this, g, 700, 23);
My main class (painting method is in the other class)
public class snake implements MouseListener{
public static int mouseX;
public static int mouseY;
public static String settingsConfig = "/assets/settings.png";
public static void main(String[] args) {
// JFrame
JFrame obj = new JFrame("Snake");
gameplay Gameplay = new gameplay();
obj.setBounds(10, 10, 905, 700);
obj.setBackground(Color.DARK_GRAY);
obj.setResizable(false);
obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
obj.add(Gameplay);
obj.setVisible(true);
obj.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if(mouseX > 699 && mouseX < 761 && mouseY > 22 + 25 && mouseY < 54 + 25) {
settingsConfig = "/assets/settingshover.png";
}
else {
settingsConfig = "/assets/settings.png";
}
}
});
}
Very small piece of paint(Graphics g) (In a different class):
public void paint (Graphics g) {
if(moves == 0) {
snakexlength[2] = 50;
snakexlength[1] = 75;
snakexlength[0] = 100;
snakeylength[2] = 100;
snakeylength[1] = 100;
snakeylength[0] = 100;
}
if(moves >= 1) {
playing = true;
}
// Draw title image border
g.setColor(Color.WHITE);
g.drawRect(24, 10, 851, 55);
// Draw the title image and settings
titleImage = new ImageIcon(getClass().getResource("/assets/snaketitle.jpg"));
titleImage.paintIcon(this, g, 25, 11);
settingsImage = new ImageIcon(getClass().getResource(settingsConfig));
settingsImage.paintIcon(this, g, 700, 23);
// Draw the border for gameplay
g.setColor(Color.WHITE);
g.drawRect(24, 74, 851, 577);
// Draw background for the gameplay
g.setColor(Color.BLACK);
g.fillRect(25, 75, 850, 575);
// Draw score
g.setColor(Color.WHITE);
g.setFont(new Font("arial", Font.PLAIN, 14));
g.drawString("Score: " + score, 780, 30);
// Draw high score
g.drawString("High Score: " + highScore, 780, 50);
}
How can I make it so that it repaints it again when the image location is changed
Invoke reapint(); in the place where you need the paintings to be updated. For example in the listener when you mouse-over the image.
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if(mouseX > 699 && mouseX < 761 && mouseY > 22 + 25 && mouseY < 54 + 25){
settingsConfig = "/assets/settingshover.png";
}
else{
settingsConfig = "/assets/settings.png";
}
repaint();
}

How to prevent my KeyEvent from repainting both objects?

I am working on a simple game which requires 1 player (the square) and some enemies that spawn randomly inside the play-area. I am running into an issue currently, because when I run my program, pressing any arrow key will repaint not only the player's new location, but it will also re-spawn all the enemies into the new locations.
I have gone through my code a few times and I am still stumped as to why this is happening. Any help would be greatly appreciated.
P.S. I am not a very experienced programmer, so some of this code may not be as efficient as possible and some things may be incorrect; feel free to point out any errors besides the issue at hand. Thanks!
Main Class
public class Eat {
public static void main(String[] args) {
// Creating the main frame
JFrame main = new JFrame("Eat 'Em All - Version 1.0.2");
main.setSize(497, 599);
main.setLocationRelativeTo(null);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setResizable(false);
// Colours and borders
Border areaBorder = new LineBorder(Color.LIGHT_GRAY, 3);
// Creating main JPanel
JPanel area = new JPanel();
area.setLayout(new BoxLayout(area, BoxLayout.PAGE_AXIS));
area.setBackground(Color.WHITE);
main.setContentPane(area);
// Creating the drawing/image/player
DrawPlayer player = new DrawPlayer();
player.setPreferredSize(new Dimension(497, 539));
player.setOpaque(false);
// Enemies
DrawEnemy enemy = new DrawEnemy();
enemy.setPreferredSize(new Dimension(497, 539));
enemy.setBackground(Color.WHITE);
// Creating the control panel for buttons, etc
JPanel control = new JPanel();
control.setPreferredSize(new Dimension(497, 60));
control.setLayout(new GridLayout(1, 2, 0, 0));
control.setBorder(areaBorder);
JLabel welcome = new JLabel(" Welcome to Eat 'Em All |--| Press 'Start'");
JButton start = new JButton("Start");
// Adding it all to the frame
main.add(enemy);
enemy.add(player);
control.add(welcome);
control.add(start);
area.add(control);
// Adding keylistener and making button false
player.addKeyListener(player);
player.setFocusable(true);
start.setFocusable(false);
enemy.setFocusable(false);
// Bring frame to front and visible
main.toFront();
main.setVisible(true);
System.out.println(player.getWidth() / 2);
System.out.println(player.getHeight() / 2);
}
}
Drawing Player Class
public class DrawPlayer extends JPanel implements KeyListener {
long xPosition = 0;
long yPosition = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Making loop to get points and move it
// Center of area is x: 245 y: 255
int xPoints[] = {235, 255, 255, 235, 235, 255};
int yPoints[] = {265, 265, 245, 245, 265, 245};
for (int i = 0; i < xPoints.length; i++) {
xPoints[i] += xPosition;
yPoints[i] += yPosition;
}
g.setColor(Color.BLUE);
g.drawPolygon(xPoints, yPoints, xPoints.length);
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_DOWN:
if (yPosition == 245) {
yPosition -= 5;
} else {
yPosition += 5;
}
break;
case KeyEvent.VK_UP:
if (yPosition == -245) {
yPosition += 5;
} else {
yPosition -= 5;
}
break;
case KeyEvent.VK_LEFT:
if (xPosition == -235) {
xPosition += 5;
} else {
xPosition -= 5;
}
break;
case KeyEvent.VK_RIGHT:
if (xPosition == 235) {
xPosition -= 5;
} else {
xPosition += 5;
}
break;
}
repaint();
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
Drawing Enemies Class
public class DrawEnemy extends JPanel {
public void paintComponent(Graphics f) {
super.paintComponent(f);
for (int i = 0; i < 10; i++ ){
f.setColor(Color.RED);
f.drawOval((int)(Math.random() * ((440 - 0) + 0) + 0), (int)(Math.random() * ((500 - 0) + 0) + 0), 50, 50);
}
}
}
Your have a problem here:
public void paintComponent(Graphics f) {
super.paintComponent(f);
for (int i = 0; i < 10; i++ ){
f.setColor(Color.RED);
f.drawOval((int)(Math.random() * ((440 - 0) + 0) + 0), (int)(Math.random() * ((500 - 0) + 0) + 0), 50, 50);
}
}
You've got program logic inside of a painting method, something you should never do, since you never have full control over when or even if a painting method will be called. The solution, get the randomization out of the paintComponent method and into its own separate method, one that you call if and only if you want to randomize the enemies, and not every time you repaint.
Other issues:
Separate your program logic from your GUI.
For instance you should have a non-GUI Enemy class, one that has fields for its own position, its size, its movement, perhaps a move() method, perhaps a collision(Player p) method.
You should have only one JPanel that does drawing and this should be its only job.
Again, you don't tie movement of anything to the painting method.
You would want a game loop of some sort, perhaps a Swing Timer. This will generate regularly spaced ticks that will prod Enemies and Players to move.
Get rid of KeyListener code and favor Key Bindings. The latter is much less kludgy when it comes to component focus. Do check the tutorial for this.

2D Game "jumping"

I have been working on a very simple 2d game in java, probably going to end up using it as a flash game or something when it's done, and I realized early on that I had no way of "jumping" for the "character" moved by the keys (a.k.a the green box in this code):
import javax.swing.*;
import java.awt.Graphics;
import javax.swing.AbstractAction;
import javax.swing.Action;
import java.awt.event.ActionEvent;
import java.awt.Color;
public class SquareGame extends JPanel{
static final int SIZE = 400;
static int x = SIZE / 2, width = 25, y = (SIZE / 2) - width, height = 25;
public static SquareGame m = new SquareGame();
public static void main(String[] args){
final JFrame frame = new JFrame();
frame.add(m);
frame.setTitle("Square Game");
frame.setSize(SIZE, SIZE);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Action actionRight = new AbstractAction(){
public void actionPerformed(ActionEvent actionRightEvent){
if(x < (SIZE - width)){
x += (width / 5);
}
}
};
Action actionLeft = new AbstractAction(){
public void actionPerformed(ActionEvent actionLeftEvent){
if(x > 0){
x -= (width / 5);
}
}
};
Action actionUp = new AbstractAction(){
public void actionPerformed(ActionEvent actionUpEvent){
if(y > ((SIZE / 2) - (height * 2))){
y -= (height / 5);
}
}
};
Action actionRelUp = new AbstractAction(){
public void actionPerformed(ActionEvent actionUpEvent){
if(y < (SIZE / 2) - height){
y += (height / 5);
}
}
};
KeyStroke keyRight = KeyStroke.getKeyStroke("RIGHT");
KeyStroke keyLeft = KeyStroke.getKeyStroke("LEFT");
KeyStroke keyUp = KeyStroke.getKeyStroke("SPACE");
KeyStroke relUp = KeyStroke.getKeyStroke("released SPACE");
InputMap inputMap = m.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(keyRight, "RIGHT");
inputMap.put(keyLeft, "LEFT");
inputMap.put(keyUp, "SPACE");
inputMap.put(relUp, "released SPACE");
m.getActionMap().put("RIGHT", actionRight);
m.getActionMap().put("LEFT", actionLeft);
m.getActionMap().put("SPACE", actionUp);
m.getActionMap().put("released SPACE", actionRelUp);
}
#Override
public void paint(Graphics g){
g.drawLine(0, SIZE / 2, SIZE, SIZE / 2);
g.setColor(Color.green);
g.fillRect(x, y, width, height);
m.repaint();
}
}
I thought adding the "key released" for the jump key would work, however that apparently only registers if the key is pressed and then released, not as long as the key is released. So, how do I add the math for jumping correctly? Also, I do realize that there may be a few other minor problems with this program, I am planning to get this all cleaned up after I figure out more with the character jumping and movement.

Zooming In On a Java Graphic

I am making a graph of a sound speed profile with java graphics. I have the static graph drawn (the one that pops up when you run the program), but I am trying to implement it such that if the user clicks on either the x or y axis, then it will zoom in so you can look at the sound speed profile more closely. I don't have the sound speed profile in the graph yet (I already know how to do that, I'm just saving the effort of drawing it until I have the zoom feature figured out). Does anyone have any ideas as to how to make this work? I have seen people trying to use the Affine Transform for similar tasks, but I am not sure that's the right thing to do or if I'm even doing this correctly. The particular code to look at is the paint(), zoomIn(), and mouseClicked() method. Ideas would be much apprecaited!
public class SoundSpeedProfile extends JPanel implements MouseListener, ActionListener {
private String title;
private String subTitle;
private JFrame frame;
private Graphics g;
.
.
.
/**Draws the sound speed profile and surrounding graphics
* #param Graphics g - graphics object
*/
public void paint(Graphics g){
this.g = g;
super.paint(g); //the super knows how to draw "standard" components like squares, rectangles, circles, etc
g.setColor(Color.DARK_GRAY);
//1) Set up the graph the sound speed profile lives in
//X-Axis for Speeds
g.drawLine(100, 150, 450, 150);//the graphics display has 0,0 in the upper left corner versus the lower left corner
int i = 120;
int k = 1460;
while (i<440){
g.drawString("|", i, 155);
g.drawString("" + k + "", i-2, 140);
k = k + 20;
i = i + 60;
}
//Y-Axis
g.drawLine(100, 500, 100, 150);
k= 7000;
int j = 500;
while (j>160){
g.drawString("" + k, 60, j);
g.drawString("--", 94, j);
k = k - 1000;
j = j - 50;
}
Font f1 = new Font("Serif", 4, 15);
g.setFont(f1);
g.drawString(this.title, 200,30);//Graph Title
g.drawString(this.subTitle, 225, 50);
Font f2 = new Font("Serif", 2, 15);
g.setFont(f2);
g.drawString("Sound Speed ", 200, 110);//x-axis label
g.drawString("(" + spdUnits + ")", 290, 110); //Units label--taken from input array
g.drawString("Depth", 10, 180); //y-axis label
g.drawString("(" + depUnits + ")", 07, 200); //Units label--taken from input array
//((Graphics2D)g).scale(20, 20);
}
/**Creates and shows the GUI drawing of the sound speed profile in a JFrame
*/
private void createAndShowGUI(){
frame = new JFrame("Sound Speed Profile");
canvas = new Canvas();
frame.add(canvas);
frame.addMouseListener(this);
frame.setBackground(Color.cyan);
frame.setSize(600,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);//add the sound speed profile graphic and set a Border Layout
//frame.pack();
frame.setVisible(true);
}
/**
* Runs test cases
* #param args
*/
public static void main (String [] args){
ssp.setTitle("Sound Speed Profile 1");
ssp.setSubtitle("June 1, 2012");
ssp.createAndShowGUI();
ssp.repaint(); //necessary?
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent arg0) {
int x = arg0.getX();
//System.out.println("x: " + x);
int xMin = x - 50;
int xMax = x + 50;
int y = arg0.getY();
//System.out.println("y: " + y);
int yMin = y-50;
int yMax = y + 50;
//If the user clicked on the x-axis
if ( 160<y && y<180 && 100<x && x<450){
System.out.println("About to zoom in on the x-axis");
zoomIn(x, y);
//System.out.println("zooming in on the x-axis");
}
//If the user clicked on the y-axis
if (90<x && x<110 && 150 <y && y<500){
//System.out.println("zooming in on the y-axis");
}
}
public void zoomIn(int x, int y){
AffineTransform old = ((Graphics2D) g).getTransform();
for (double zoom = 1; zoom >=0.1; zoom=-0.1){
AffineTransform tr2 =AffineTransform.getTranslateInstance(-x, -y);
AffineTransform tr= AffineTransform.getScaleInstance(zoom,zoom);
tr.concatenate(tr2); tr2=tr;
tr =AffineTransform.getTranslateInstance(x, y);
tr.concatenate(tr2); tr2=tr;
tr= new AffineTransform(old);
tr.concatenate(tr2); tr2=tr;
((Graphics2D)g).setTransform(tr2);
((Graphics2D)g).drawRect(x, y, 10, 10);
((Graphics2D)g).setTransform(old);
}
}

Stuck on Design

I have a small GUI java program I'm working on but designing how the pieces should work together is frustrating me. Basically what I need to do is draw a "car" on a panel and attach that panel to a frame. Then animate the panel to give the illusion of movement.
My problem is that I don't understand how to move the code that does the animation to its own class. When I started working on this I made the UI elements first. Then I came across the Timer class and used that class in what was supposed to be just a UI element class (CarBody). The way my code works now is that as soon as I run the program the "car" starts moving because I don't have it set up to be triggered by a button press. I don't understand how to move the animation code to its on class and trigger it with a button press.
I could solve this in two seconds IF I could call repaint() in the performAction() method. The problem is I can't do that! It wont compile that way.
What I have done is
class CarBody extends JPanel {
private int xCoordinate = 0;
CarBody(){
Timer timer = new Timer(1000,new TimerListener());
timer.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
//g.fillRect(10, 10, 60, 50);
if(xCoordinate > getWidth()){
xCoordinate = -20;
}
xCoordinate +=100;
g.fillRect(xCoordinate,10,60,50);
}
class TimerListener implements ActionListener{
public void actionPerformed(ActionEvent e){
repaint();
}
}
}
I could solve this in two seconds IF I could call repaint() in the performAction() method.
You should not be changing the car coordinates in the paintComponent() method. You should have a method that sets the coordinates and then repaints the car.
When you create the ActionListener class then pass in the panel you want to repaint as a parameter to the class. Then you can invoke the "changeLocation" method which will update the location of the car and then invoke repaint() on itself.
For another approach you can add an Icon to a label and just change the location of the label and it will repaint itself automatically. Here is a simple example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerAnimation extends JLabel implements ActionListener
{
int deltaX = 2;
int deltaY = 3;
int directionX = 1;
int directionY = 1;
public TimerAnimation(
int startX, int startY,
int deltaX, int deltaY,
int directionX, int directionY,
int delay)
{
this.deltaX = deltaX;
this.deltaY = deltaY;
this.directionX = directionX;
this.directionY = directionY;
setIcon( new ImageIcon("dukewavered.gif") );
// setIcon( new ImageIcon("copy16.gif") );
setSize( getPreferredSize() );
setLocation(startX, startY);
new javax.swing.Timer(delay, this).start();
}
public void actionPerformed(ActionEvent e)
{
Container parent = getParent();
// Determine next X position
int nextX = getLocation().x + (deltaX * directionX);
if (nextX < 0)
{
nextX = 0;
directionX *= -1;
}
if ( nextX + getSize().width > parent.getSize().width)
{
nextX = parent.getSize().width - getSize().width;
directionX *= -1;
}
// Determine next Y position
int nextY = getLocation().y + (deltaY * directionY);
if (nextY < 0)
{
nextY = 0;
directionY *= -1;
}
if ( nextY + getSize().height > parent.getSize().height)
{
nextY = parent.getSize().height - getSize().height;
directionY *= -1;
}
// Move the label
setLocation(nextX, nextY);
}
public static void main(String[] args)
{
JPanel panel = new JPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().setLayout(null);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
// frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
// frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
}
}
This example, doesn't do custom painting and the animation is done by invoking the setlocation(...) method on the label which will cause a repaint() so the solution is slightly different than yours, but the key point is NOT to change the location values in the paintComponent() method.

Categories

Resources