Java Applet: Cant figure out how to display rectangle on keypressed - java

I am basically coding this basic arcade game and i need the circle to shoot out small rectangles that looks like bullets or missiles to hit the bad guys whenever the space bar is hit but i cant figure out how.
Heres my code so far:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class main extends Applet implements Runnable, KeyListener {
private Image i;
private Graphics doubleG;
// x and y are used to set (x,y) positions
// dx and dy are the changes in position
private int x = 850;
private int y = 850;
private int x2 = 100;
private int y2 = 100;
private int dx = 50;
private int radius = 30;
private int dx2 = 4;
private int dy2 = 4;
private int x3 = 100;
private int y3 = 200;
private int dx3 = 5;
private int dy3 = 5;
private int x4 = 100;
private int y4 = 300;
private int dx4 = 3;
private int dy4 = 3;
public void init(){
setSize(1920,1080);
setBackground(Color.black);
addKeyListener(this);
}
public void start(){
Thread thread = new Thread(this);
thread.start();
}
public void run() {
while(true){
//Enemy
if(x2 + dx2 > this.getWidth() - radius - 1){
x2 = this.getWidth() - radius - 1;
dx2 = -dx2;
}
if(x2 + dx2 < 0 + radius){
x2 = 0 + radius;
dx2 = -dx2;
}
x2 += dx2;
// Enemy
if(x3 + dx3 > this.getWidth() - radius - 1){
x3 = this.getWidth() - radius -1;
dx3 = -dx3;
}
if(x3 + dx3 < 0 + radius){
x = 0 + radius;
dx3 = -dx3;
}
x3 += dx3;
// Enemy
if(x4 + dx4 > this.getWidth() - radius - 1){
x4= this.getWidth() - radius -1;
dx4 = -dx4;
}
if(x4 + dx4 < 0 + radius){
x4 = 0 + radius;
dx4 = -dx4;
}
x4 += dx4;
// EVERYTHING ABOVE KEEPS ASTEROIDS IN THE SCREEN ALLOWING IT TO BOUNCE OFF WALLS
repaint();
try{
Thread.sleep(17);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
public void stop(){
}
public void update(Graphics g){
// this function stops the flickering problem every time the ball moves by copying the image instead of repainting it
if(i == null){
i = createImage(this.getSize().width, this.getSize().height);
doubleG = i.getGraphics();
}
doubleG.setColor(getBackground());
doubleG.fillRect(0,0,this.getSize().width, this.getSize().height);
doubleG.setColor(getForeground());
paint(doubleG);
g.drawImage(i,0,0,this);
}
public void paint(Graphics g){
g.setColor(Color.BLUE);
g.fillOval(x, y, radius*2, radius*2);
g.setColor(Color.RED);
g.fillOval(x2, y2, radius + 10, radius + 10);
g.setColor(Color.RED);
g.fillOval(x3,y3, radius + 10, radius + 10);
g.setColor(Color.RED);
g.fillOval(x4, y4, radius + 10, radius + 10);
}
public void moveRight(){
if (dx-1 > -20){
dx += 1;
}
if(x + dx > this.getWidth() - radius - 1){
x = this.getWidth() - radius - 1;
dx = -dx;
}
x += dx;
}
public void moveLeft(){
if(dx - 1 > -20){
dx -= 1;
}
if(x + dx < 0 + radius){
x = 0 + radius;
dx = -dx;
}
x -= dx;
}
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
moveLeft();
break;
case KeyEvent.VK_RIGHT:
moveRight();
break;
}
}
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}

KeyListener will only raise KeyEvents if the component it is registered to is focusable AND has foucs.
You never call super.paint, expect some serious paint artifacts
Avoid overriding paint of top level containers (like Applet)
Consider using Swing based components over AWT, apart from been more update to date and more widely used, Swing components are also double buffered by default. Use a combination of JApplet and JPanel as the main drawing surface, overriding it's paintComponent method. In this case, also consider using a javax.swing.Timer over Thread, unless you want to try and maintain a variable delay between updates. This would also allow you to use the key bindings API overcoming the focus issues related to KeyListener

Related

How do I make it so the circle in this frame actually moves automatically?

I can't get around this problem. I've created this frame and panel with my desired output. Only thing is that I haven't been able to figure out was how to make the ball move "automatically". My ideal game would have the ball/circle dropping at the start of the game, perhaps with a click of a button or such. How would I go about doing this?
I've tried to move the ball with the E key, but that would be too inconvenient for the user, so I figured that having it move without an event handler would be a better choice.
private int ballX, ballY, ballR;
private int score1, score2;
private JPanel panel;
private JFrame frame;
private DrawingArea canvas;
private int xpos, ypos,width,height;
private int xpos2, ypos2, width2, height2;
public Pong(){
xpos = 300;
ypos = 550;
width = 100;
height = 50;
xpos2 = 300;
ypos2 = 100;
width2 = 100;
height2 = 50;
ballR = 50;
ballX = 325;
ballY = 330;
}
public static void main(String[]args){
Pong p = new Pong();
p.run();
}
public void run(){
frame = new JFrame("Pong");
frame.setSize(700,700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas = new DrawingArea(); // create a panel to draw on
canvas.setBackground(Color.WHITE);
canvas.addFocusListener(this);
canvas.addKeyListener(this);
canvas.addMouseListener(this);
frame.getContentPane().add(canvas);
frame.setVisible(true);
}
class DrawingArea extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor ( Color.blue );
g.fillRect ( xpos, ypos, width, height );
g.setColor(Color.red);
g.fillRect(xpos2, ypos2, width2, height2);
g.setColor(Color.black);
g.fillRect(0,0,700,50);
g.fillRect(0,630,700,50);
g.fillOval(ballX,ballY,ballR, ballR);
}
}
public void keyPressed ( KeyEvent e ) {
int value = e.getKeyCode();
switch ( value ) {
case KeyEvent.VK_RIGHT: xpos += 50; break;
case KeyEvent.VK_LEFT: xpos -= 50; break;
case KeyEvent.VK_A: xpos2 -= 50; break;
case KeyEvent.VK_D: xpos2 += 50; break;
/*try to drop the ball with the space button
* case KeyEvent.VK_SPACE:
ballX+=25;
ballY+=25;
break;
case KeyEvent.VK_ENTER:
xpos = (int)( Math.random ( ) * ( 500 - (2 * radius) ) );
ypos = (int)( Math.random ( ) * ( 500 - (2 * radius) ) );
break;
*/
}
if( (xpos < 0 || xpos >= 700) || (xpos2 < 0 || xpos2 >= 700)){
if(xpos < 0 || xpos2 < 0){
if(xpos < 0) xpos = 0;
else if(xpos2 < 0) xpos2 = 0;
return;
}
else if(xpos >= 700 || xpos2 >= 700){
if(xpos >= 700)xpos = 550;
else if(xpos2 >= 700) xpos2=550;
return;
}
}
canvas.repaint ( );
}
}
I expect the output to have the ball drop down on to the blue rectangle with the start of the game/press of a button, but I can't get that to work.
As you want to move the ball automatically, in run() method change position of the ball(Your xpos, ypos, xpos1 and ypos2) with some increment values(as you have done in keyPressed() interface method) dx, dy etc. and invoke repaint() method of your JFrame.
public class BouncingBall extends JPanel {
// Box height and width
int width;
int height;
// Ball Size
float radius = 40;
float diameter = radius * 2;
// Center of Call
float X = radius + 50;
float Y = radius + 20;
// Direction
float dx = 3;
float dy = 3;
public BouncingBall() {
Thread thread = new Thread() {
public void run() {
while (true) {
width = getWidth();
height = getHeight();
X = X + dx ;
Y = Y + dy;
if (X - radius < 0) {
dx = -dx;
X = radius;
} else if (X + radius > width) {
dx = -dx;
X = width - radius;
}
if (Y - radius < 0) {
dy = -dy;
Y = radius;
} else if (Y + radius > height) {
dy = -dy;
Y = height - radius;
}
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
}
}
}
};
thread.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval((int)(X-radius), (int)(Y-radius), (int)diameter, (int)diameter);
}
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Bouncing Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setContentPane(new BouncingBall());
frame.setVisible(true);
}
}
As I have used render time of 50 ms in Thread.sleep(), you can specify your own time. So for me each 50 ms your JFrame gets updated.

Pixels not drawing in java applet

I wrote this program that is supposed to draw a Sierpinski triangle, however whenever I open it with appletviewer on a lower-end computer it will usually draw anywhere from 4 to 60 dots. If I re-open it without re-compiling it the number of dots will occasionally go up from the usual 4. Running it on my higher-end computer at home it works best at around 5 000 000 iterations. However, although it draws the shape fairly full, it starts to draw dots where it should not be possible for there to be dots. This only occurs when the numbers start to get higher. Why is my applet not working on lower end computers, and why does it draw pixels in the incorrect positions at greater values? Here is the code:
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
public class Triangle extends Applet implements Runnable {
Thread runner;
Random rand = new Random();
int xCord, yCord;
double lastX, lastY, currentX, currentY;
final double topX = 500, topY = 100, leftX = 200, leftY = 700,
rightX = 800, rightY = 700;
public void start() {
if (runner == null)
{
runner = new Thread(this);
runner.start();
}
}
public void init() {
lastX = 500; //Top dot
lastY = 100;
}
public void run() {
for(int counter = 1; counter <= 5000000; counter++) {
int n = rand.nextInt(3) + 1;
if (n == 1) { //Top dot
currentX = topX + (lastX - topX) / 2;
currentY = topY + (lastY - topY) / 2;
lastX = currentX;
lastY = currentY;
xCord = (int)Math.round(currentX);
yCord = (int)Math.round(currentY);
repaint();
} else if (n == 2) { //Left dot
currentX = leftX + (lastX - leftX) / 2;
currentY = leftY + (lastY - leftY) / 2;
lastX = currentX;
lastY = currentY;
xCord = (int)Math.round(currentX);
yCord = (int)Math.round(currentY);
repaint();
} else { //Right dot
currentX = rightX + (lastX - rightX) / 2;
currentY = rightY + (lastY - rightY) / 2;
lastX = currentX;
lastY = currentY;
xCord = (int)Math.round(currentX);
yCord = (int)Math.round(currentY);
repaint();
}
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
g.fillRect(500, 100, 2, 2); //Top dot, dot 1
g.fillRect(200, 700, 2, 2); //Bottom left dot, dot 2
g.fillRect(800, 700, 2, 2); //Bottom right dot, dot 3
g.fillRect(xCord, yCord, 2, 2);
}
}
Here is my HTML file:
<html>
<body>
<applet code = "Triangle.class" width=999 height=799>
</applet>
</body>
</html>

Java GUI Window Displays Garbage

I wrote a Java program to take a triangle and either rotate, shift, or rotate and shift it, based upon a button click preformed by the user.
Beforehand, I instruct the user to enter in ranges of logical coordinates to determine how pixel coordinates will map to a real x-y coordinate system.
Initially, I have the triangle appearing in the middle of the screen, and after a button is clicked, the triangle is shown after a certain operation is preformed on it (i.e rotation, shifting, etc.)
However, after the operation is completed and the triangle is redrawn, I see an input box also drawn in the top-left corner of the JPanel.
I'm not sure how this keeps getting drawn there.
Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class RotateAndShiftTriangles extends JFrame {
public static void main(String[] args) { new RotateAndShiftTriangles(); }
RotateAndShiftTriangles() {
super("Drawing 50 Triangles");
final JPanel drawingPanel = new DrawTriangles();
JPanel buttonPanel = new JPanel();
JButton rotate = new JButton("Rotate"),
shift = new JButton("Shift"),
rotateShift = new JButton("Rotate and Shift"),
reset = new JButton ("Reset");
drawingPanel.setBackground(Color.WHITE);
buttonPanel.add(rotate);
buttonPanel.add(shift);
buttonPanel.add(rotateShift);
buttonPanel.add(reset);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
DrawTriangles.rWidth = Float.parseFloat(JOptionPane.showInputDialog("Input rWidth"));
DrawTriangles.rHeight = Float.parseFloat(JOptionPane.showInputDialog("Input rHeight"));
rotate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DrawTriangles.rotate = true;
drawingPanel.repaint();
}
});
shift.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DrawTriangles.shift = true;
drawingPanel.repaint();
}
});
rotateShift.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DrawTriangles.rotate = true;
DrawTriangles.shift = true;
drawingPanel.repaint();
}
});
reset.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
DrawTriangles.reset = true;
drawingPanel.repaint();
}
});
setSize(600, 400);
add("South", buttonPanel);
add("Center", drawingPanel);
setVisible(true);
}
}
class DrawTriangles extends JPanel {
static float rWidth, rHeight, pixelSize;
static int maxX, maxY, minMaxXY, centerX, centerY;
static boolean rotate = false, shift = false, reset = false;
float angle = 0;
void initialize() {
Dimension d = getSize();
maxX = d.width - 1; maxY = d.height - 1;
pixelSize = Math.max(rWidth / maxX, rHeight / maxY);
minMaxXY = Math.min(maxX, maxY);
centerX = maxX/2; centerY = maxY/2;
}
public int iX2(float x) { return Math.round(x); }
public int iY2(float y) { return maxY - Math.round(y); }
public static int iX(float x) { return Math.round(centerX + x / pixelSize); }
public static int iY(float y) { return Math.round(centerY - y / pixelSize); }
public static float fx(int x) { return (x - centerX) * pixelSize; }
public static float fy(int y) { return (centerY - y) * pixelSize; }
public void paint(Graphics g) {
super.paintComponent(g);
initialize();
int left = iX(-rWidth/2), right = iX(rWidth/2);
int top = iY(rHeight/2), bot = iY(-rHeight/2);
g.drawString("X: " + -rWidth/2 + " Y: " + rHeight/2, left, top + 10);
g.drawString("X: " + rWidth/2 + " Y: " + rHeight/2, right - 55, top + 10);
g.drawString("X: " + -rWidth/2 + " Y: " + -rHeight/2, left, bot);
g.drawString("X: " + rWidth/2 + " Y: " + -rHeight/2, right - 55, bot);
g.setColor(Color.BLUE);
g.drawRect(left, top, right - left, bot - top);
float side = 0.95f * minMaxXY, sideHalf = 0.5F * side,
h = sideHalf * (float)Math.sqrt(3),
xA, yA, xB, yB, xC, yC,
xA1, yA1, xB1, yB1, xC1, yC1, p, q;
q = 0.05F;
p = 1 - q;
xA = centerX - sideHalf;
yA = centerY - 0.5F * h;
xB = centerX + sideHalf;
yB = yA;
xC = centerX;
yC = centerY + 0.5F * h;
if(!reset) {
if(rotate) {
angle += Float.parseFloat(JOptionPane.showInputDialog("Input Angle of Rotation (in degrees)"));
float xR = fx(Integer.parseInt(JOptionPane.showInputDialog("Input X Coordinate for Rotation"))),
yR = fx(Integer.parseInt(JOptionPane.showInputDialog("Input Y Coordinate for Rotation")));
xA = rotateX(xA, yA, xR, yR, angle);
yA = rotateY(xA, yA, xR, yR, angle);
xB = rotateX(xB, yB, xR, yR, angle);
yB = rotateY(xB, yB, xR, yR, angle);
xC = rotateX(xC, yC, xR, yR, angle);
yC = rotateY(xC, yC, xR, yR, angle);
rotate = false;
}
if(shift) {
float xShift = -fx(Integer.parseInt(JOptionPane.showInputDialog("Input X Coordinate for Shift"))),
yShift = -fx(Integer.parseInt(JOptionPane.showInputDialog("Input Y Coordinate for Shift")));
xA += xShift;
yA += yShift;
xB += xShift;
yB += yShift;
xC += xShift;
yC += yShift;
shift = false;
}
}
g.setColor(Color.RED);
for (int i = 0; i < 50; i++) {
g.drawLine(iX2(xA), iY2(yA), iX2(xB), iY2(yB));
g.drawLine(iX2(xB), iY2(yB), iX2(xC), iY2(yC));
g.drawLine(iX2(xC), iY2(yC), iX2(xA), iY2(yA));
if(i == 0) {
g.setColor(Color.BLACK);
g.drawString("A: X- " + xA + " Y- " + yA, 0, 50);
g.drawString("B: X- " + xB + " Y- " + yB, 0, 60);
g.drawString("C: X- " + xC + " Y- " + yC, 0, 70);
g.setColor(Color.RED);
}
xA1 = p * xA + q * xB;
yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC;
yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA;
yC1 = p * yC + q * yA;
xA = xA1; xB = xB1; xC = xC1;
yA = yA1; yB = yB1; yC = yC1;
}
if(reset)
angle = 0;
reset = false;
}
public float rotateX(float x, float y, float xR, float yR, float angle) {
angle *= (Math.PI / 180.0);
float c = (float)Math.cos(angle), s = (float)Math.sin(angle),
xF = x - xR, yF = y - yR,
rx = xF * c - yF * s;
return rx + xR;
}
public float rotateY(float x, float y, float xR, float yR, float angle) {
angle *= (Math.PI / 180.0);
float c = (float)Math.cos(angle), s = (float)Math.sin(angle),
xF = x - xR, yF = y - yR,
ry = xF * s + yF * c;
return ry + yR;
}
}
I keep getting this
You are triggering JOptionPane popups inside your paint() method.
Calls to .paint() and its siblings should limit themselves to redrawing the object, nothing else. As is, your code will cause your .paint() method to block until the popup is closed, then continue processing where it left off, potentially picking up artifacts still on the screen. As you can see here, the background is painted (by the call to super.paintComponent()) then the popup is drawn and closed, then the rest of your .paint() method runs, but since the background has already been painted, nothing repaints over where the popup was.
You should move code like:
angle += Float.parseFloat(JOptionPane.showInputDialog("Input Angle of Rotation (in degrees)"));
float xR = fx(Integer.parseInt(JOptionPane.showInputDialog("Input X Coordinate for Rotation"))),
yR = fx(Integer.parseInt(JOptionPane.showInputDialog("Input Y Coordinate for Rotation")));
and
float xShift = -fx(Integer.parseInt(JOptionPane.showInputDialog("Input X Coordinate for Shift"))),
yShift = -fx(Integer.parseInt(JOptionPane.showInputDialog("Input Y Coordinate for Shift")));
out into the appropriate ActionListener methods, set the necessary values, and then use them from within your paint() method.
You should also be consistent about using .paint() and .paintComponent(), like #camickr suggests, don't have one method call its sibling's super.
public void paint(Graphics g) {
super.paintComponent(g);
Don't know if it is the only problem but, custom painting is done by overriding the paintComponent() method:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Edit:
Other comments, not directly related to the problem, but important for proper design:
add("South", buttonPanel);
add("Center", drawingPanel);
Don't use hard coded literals. The layout manager will provide variable you can use. Also, that form of the add(...) method is not recommended (read the API). The new form is:
add(buttonPanel, BordeLayout.PAGE_END);
add("Center", BorderLayout.CENTER);
Don't use static methods and variables. If you want to change a property of your class then create "setter" method. For example create a setter method:
public void setRotate(Boolean rotate)
{
this.rotate = rotate
repaint();
}
Also, not that the setter method invokes the repaint() method. This is because your custom class (not the code that uses the class) should be responsible for doing the repaint.
Then invoke the setter method:
//DrawTriangles.rotate = true; // wrong
drawingPanel.setRotate(true);
Looks like this only happens if dialogs are displayed. I've modified the code and hardcoded some values, it worked without problems.
if(!reset) {
if(rotate) {
angle += Float.parseFloat("15");
float xR = fx(3),
yR = fx(3);
// other stuff...
}
I suggest you try displaying dialogs and setting corresponding values before repainting the components, something similar to this:
shift.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
float xShift = -DrawTriangles.fx(Integer.parseInt(JOptionPane.showInputDialog("Input X Coordinate for Shift"))),
yShift = -DrawTriangles.fx(Integer.parseInt(JOptionPane.showInputDialog("Input Y Coordinate for Shift")));
drawingPanel.xShift = xShift;
drawingPanel.yShift = yShift;
DrawTriangles.shift = true;
drawingPanel.repaint();
}
});
Using BufferedImage corrected the drawing but still the exception occurred.
public void paint(Graphics gg) {
BufferedImage bf = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = bf.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
...
gg.drawImage(bf, 0, 0, null);
if(reset)
angle = 0;
reset = false;
}

Trying to create a Pong game

I'm trying to create a pong game in Java, and I'm having a little trouble with the ball physics. It all works pretty much perfectly, until the ball gets too slow. I made it so the ball decreases in speed by 0.02 pixels per frame per frame, but I don't want the it to get too slow, so I have a check that prevents it from decelerating once the x and y velocities get below 2. The problem is once this happens, it doesn't keep the current trajectory if the x or y velocity is significantly smaller than the other. It seems to smooth out so that it is almost as straight line, except for moving up or down 1 pixel every few seconds. Here is my ball class:
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Ball {
private static int DIAMETER = 30;
double x = 0;
double y = 0;
double xVel = 5;
double yVel = 5;
double prevX = 0;
double prevY = 0;
double minSpeed = 2;
double maxSpeed = 10;
int drawX = 0;
int drawY = 0;
boolean tooSlow = false;
public game game;
public Ball(game game) {
this.game = game;
}
public void move()
{
prevX = xVel;
prevY = yVel;
if(xVel > maxSpeed)
xVel = maxSpeed;
if(yVel > maxSpeed)
yVel = maxSpeed;
if(Math.abs(xVel) < minSpeed && Math.abs(yVel) < minSpeed)
tooSlow = true;
else
tooSlow = false;
if(x + xVel < 0){
xVel *= -1;
}
if(x + xVel > 484 - DIAMETER){
xVel *= -1;
}
if(y + yVel < 0){
yVel *= -1;
}
if(y + yVel > 261 - DIAMETER){
yVel *= -1;
}
if(collision()){
if(game.paddle.paddleTop().intersects(getBounds())){
y += game.paddle.y - game.paddle.prevY;
yVel = -1 * Math.abs(yVel) + (game.paddle.y - game.paddle.prevY);
}
if(game.paddle.paddleBottom().intersects(getBounds())){
y += game.paddle.y - game.paddle.prevY;
yVel = Math.abs(yVel) + (game.paddle.y - game.paddle.prevY);
}
if(game.paddle.paddleRight().intersects(getBounds())){
x += game.paddle.x - game.paddle.prevX;
xVel = Math.abs(xVel) + (game.paddle.x - game.paddle.prevX);
yVel += (game.paddle.y - game.paddle.prevY) / 2;
}
if(game.paddle.paddleLeft().intersects(getBounds())){
x += game.paddle.x - game.paddle.prevX;
xVel = -1 * Math.abs(xVel) + (game.paddle.x - game.paddle.prevX);
yVel += (game.paddle.y - game.paddle.prevY) / 2;
}
}
x += xVel;
y += yVel;
if(!tooSlow)
slow();
drawX = (int)x;
drawY = (int)y;
}
public void slow()
{
xVel = (Math.abs(xVel) - 0.02) * Math.signum(xVel);
yVel = (Math.abs(yVel) - 0.02) * Math.signum(yVel);
}
public void paint(Graphics2D g) {
g.fillOval(drawX, drawY, 30, 30);
}
public Rectangle getBounds()
{
return new Rectangle(drawX, drawY, DIAMETER, DIAMETER);
}
public boolean collision()
{
return game.paddle.getBounds().intersects(getBounds());
}
}
These are the rest that I use to run the game (in case it helps):
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
public class Paddle {
int x = 50;
int y = 10;
int xVel = 0;
int yVel = 0;
int prevX = 50;
int prevY = 10;
int speed = 4;
private static final int WIDTH = 10;
private static final int HEIGHT = 80;
private game game;
public Paddle(game game)
{
this.game = game;
}
public void move()
{
prevX = x;
prevY = y;
if(y + yVel > 0 && y + yVel < 261 - HEIGHT){
y += yVel;
}
if(x + xVel > 0 && x + xVel < 484 - WIDTH){
x += xVel;
}
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP){
yVel = -speed;
}
if(e.getKeyCode() == KeyEvent.VK_DOWN){
yVel = speed;
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){
xVel = -speed;
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
xVel = speed;
}
}
public void keyReleased(KeyEvent e)
{
yVel = 0;
xVel = 0;
}
public void paint(Graphics2D g)
{
g.fillRect(x, y, WIDTH, HEIGHT);
}
public Rectangle getBounds()
{
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public Rectangle paddleTop()
{
return new Rectangle(x + WIDTH / 2, y, 1, 1);
}
public Rectangle paddleLeft()
{
return new Rectangle(x, y, 1, HEIGHT);
}
public Rectangle paddleRight()
{
return new Rectangle(x + WIDTH, y, 1, HEIGHT);
}
public Rectangle paddleBottom()
{
return new Rectangle(x + WIDTH / 2, y + HEIGHT, 1, 1);
}
public Rectangle test()
{
return new Rectangle(x, y, WIDTH, HEIGHT);
}
}
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.Timer;
public class game extends JPanel implements ActionListener, KeyListener{
private Timer t;
Ball ball = new Ball(this);
Paddle paddle = new Paddle(this);
public game()
{
t = new Timer(15,this);
t.start();
addKeyListener(this);
setFocusable(true);
}
public void actionPerformed(ActionEvent e)
{
ball.move();
paddle.move();
repaint();
}
public void keyPressed(KeyEvent e)
{
paddle.keyPressed(e);
}
public void keyReleased(KeyEvent e)
{
paddle.keyReleased(e);
}
public void keyTyped(KeyEvent e) {}
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
ball.paint(g2d);
paddle.paint(g2d);
}
}
import javax.swing.JFrame;
public class main {
public static void main(String[]args)
{
JFrame frame = new JFrame("MLG Pong");
game display = new game();
frame.add(display);
frame.setSize(500,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
System.out.println(""+display.getHeight()+" "+display.getWidth());
}
}
Sorry for my messy coding, I only learned Java a few months ago.
The problem is in the slow() method:
public void slow()
{
xVel = (Math.abs(xVel) - 0.02) * Math.signum(xVel);
yVel = (Math.abs(yVel) - 0.02) * Math.signum(yVel);
}
You are in fact substracting a vector with another direction from the velocity vector. At the beginning, the change in direction is small, but later it becomes bigger. You always substract (+-0.02, +-0.02) from the velocity vector.
An easy fix would be:
public void slow()
{
xVel = xVel*0.99;
yVel = yVel*0.99;
}
but this slows down big velocities faster than small ones.
If you don't want this behaviour, you have to calculate a small vector of constant length that has the same direction as the velocity vector, and substract this.
For example:
public void slow()
{
double len = Math.sqrt(xVel*xVel + yVel*yVel);
double sx = xVel/len*0.02;
double sy = yVel/len*0.02;
xVel = xVel - sx;
yVel = yVel - sy;
}
You don't have to check the velocity in x and y direction separately. You have to calc the velocity of the ball (in flight direction) and check the result against the minimum Speed.
Change the line
if(Math.abs(xVel) < minSpeed && Math.abs(yVel) < minSpeed)
to
if(xVel*xVel + yVel*yVel < minSpeed * minSpeed)
There is an error in the slow down calculation. When the speed is very slow abs(v)-0.02 is a non-sense.
By the way, the way you calculate is not good. You should modify the vector not directly their projections:
Take a velocity vector: vv=(length,angle). Then modify the length as you want, either by substracting some constant or use a factor on each frame. Then vx=length*cos(angle) and vy=length*sin(angle).

Moving ball at an Angle

I am a newbie and trying to move a ball at a slope. Here is the code where I am giving coordinates (100,300,300,600) to move the ball in a slope but it's getting away from the the slope line. Here's the code: -
import javax.swing.*;
import java.awt.*;
public class AgentMotion extends JPanel implements Runnable
{
Color color = Color.red;
int dia = 0;
long delay = 40;
private double x;
private double y;
private double x1;
private double y1;
private int dx = 1;
private int dy = 1;
private int dv = 1;
private double direction;
double a;
double b;
double a1;
double b1;
public void abc(double x, double y, double x2, double y2) {
this.x = x;
this.y = y;
this.x1 = x2;
this.y1 = y2;
this.direction=Math.toRadians(Math.atan2(x1-x,y1-y));
System.out.println("segfewg"+direction);
this.a = x;
this.b = y;
this.a1 = x1;
this.b1 = y1;
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
//int x=100;
//int y=200;
int x3=(int)this.x;
int y3=(int)this.y;
g.fillOval(x3,y3,5,5); // adds color to circle
g.setColor(Color.black);
g2.drawOval(x3,y3,5,5); // draws circle
g2.drawLine(100, 300, 300, 600);
}
public void run() {
if(direction<0)
{
System.out.println("refregreg");
while(dy!=0) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move2();
repaint();
move();
repaint();
}
}
else
{
while(dx!=0) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move2();
repaint();
move();
repaint();
}
}
}
public void move() {
if(direction>0)
{
if(x + dv*Math.cos(direction) <a ||x + dia + dv * Math.cos(direction) >b) {
dx *= 0;
color = getColor();
}
x += dx;
}
else
{
System.out.println(x + dia + dv * Math.cos(direction));
if(x + dia + dv * Math.cos(direction) >b) {
dx *= 0;
color = getColor();
}
x -= dx;
}
}
public void move2() {
if(direction>0)
{
if(dv * Math.sin(direction) + y <a1 || dv * Math.sin(direction) + dia + y > b1) {
dy *= 0;
color = getColor();
}
y += dy;
}
else
{
System.out.println(dv * Math.sin(direction) + dia + y);
if(dv * Math.sin(direction) + y <a1 || dv * Math.sin(direction) + dia + y < b1) {
dy *= 0;
color = getColor();
}
y -= dy;
}
}
public Color getColor() {
int rval = (int)Math.floor(Math.random() * 256);
int gval = (int)Math.floor(Math.random() * 256);
int bval = (int)Math.floor(Math.random() * 256);
return new Color(rval, gval, bval);
}
public void start() {
while(dx==0) {
try {
System.out.println("jiuj");
Thread.sleep(25);
} catch(InterruptedException e) {
System.out.println("dwdwdwd");
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
}
A few suggestions
dx and dy should be double
atan is overkill as #Spektre says. What you want is much more simple to calculate direction.
this.direction = (y2 - y) / (x2 - x)
If we give (100,300,300,600) as the arguments to abc (which would presumably be a constructor for this class), the slope is 1.5. Therefore we could say:
dx = 1.0
dy = 1.5
and this would keep on the line perfectly. For the general case where you want to change the begin and the end point, you have to calculate the ratio. So you could set either dx or dy to 1.0 and then set the other one so that the ratio is maintained. Something like the following mathematical pseudocode:
dx = (x2 - x) / minimum(x2-x, y2-y)
dy = (y2 - y) / minimum(x2-x, y2-y)

Categories