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.
Related
For the purposes of my project, I'm trying to simulate a phyllotaxis pattern by creating multiple circles in real time using the formulas given.
So recently, I've decided to try out GUI programming in Java using JFrame and swing, and I've hit a wall trying to figure out how to get everything running properly. My idea was to slowly print out circle after circle with their x and y coordinates being calculated from the "r = cos/sin(theta)" formulas documented in the phyllotaxis instructions. Unfortunately, while the x and y values are constantly changing, only one circle is printed. Is there something I am missing?
package gExample;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.Timer;
public class GraphicsExample extends Canvas implements ActionListener {
private final static int HEIGHT = 600;
private final static int WIDTH = 600;
private int n = 0;
private int x, y;
Timer t = new Timer(20, this);
public static void main(String args[]) {
JFrame frame = new JFrame();
GraphicsExample canvas = new GraphicsExample();
canvas.setSize(WIDTH, HEIGHT);
frame.add(canvas);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas.setBackground(Color.black);
}
public void paint(Graphics g){
Random rand = new Random();
Color col = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
g.setColor(col);
/*each time paint() is called, I expect a new circle to be printed in the
x and y position that was updated by actionPerformed(), but only one inital circle is created. */
g.fillOval(x, y, 8, 8);
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
int c = 9;
double r = c * Math.sqrt(n);
double angle = n * 137.5;
//here, every time the method is called, the x and y values are updated,
//which will be used to fill in a new circle
int x = (int) (r * Math.cos(angle * (Math.PI / 180) )) + (WIDTH / 2);
int y = (int) (r * Math.sin(angle * (Math.PI / 180) )) + (HEIGHT / 2);
//when the program is running, this line of code is executed multiple times.
System.out.println("x: " + x + " y: " + y);
n++;
}
}
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.
I'm making a simple Kakuro application in Java Swing and I have used JButtons as cells. I've done everything from generating the grid (setBackground(Color.BLACK) and setBackground(Color.WHITE)) to filling with unique numbers.
But the problem is, I don't know how to paint the "clues" at the ends of JButtons. What I want is similar to:
Sometimes the numbers may appear on only 3 sides, 2 sides or even 1 side.
I thought of setting background images, but its not possible as the numbers are sums of dynamically generated numbers (the grid is dynamic).
So any idea how to get this kind of JButton? Or if its not possible, what other options do I have?
Thanks a lot in advance (I'm really stuck).
very simple and confortable way is add JLabels to the JButton by using BorderLayout,
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class FourLabelsInButton {
private JFrame frame;
private JButton myButton1;
private JLabel myButton1_Label_N;
private JLabel myButton1_Label_E;
private JLabel myButton1_Label_W;
private JLabel myButton1_Label_S = new JLabel();
public FourLabelsInButton() {
myButton1_Label_N = new JLabel("45");
myButton1_Label_N.setHorizontalAlignment(JLabel.CENTER);
myButton1_Label_N.setForeground(Color.red);
myButton1_Label_E = new JLabel("1");
myButton1_Label_E.setHorizontalAlignment(JLabel.CENTER);
myButton1_Label_E.setForeground(Color.red);
myButton1_Label_W = new JLabel("9");
myButton1_Label_W.setHorizontalAlignment(JLabel.CENTER);
myButton1_Label_W.setForeground(Color.red);
myButton1_Label_S = new JLabel("21");
myButton1_Label_S.setHorizontalAlignment(JLabel.CENTER);
myButton1_Label_S.setForeground(Color.red);
myButton1 = new JButton();
myButton1.setBackground(Color.black);
myButton1.setLayout(new BorderLayout());
myButton1.add(myButton1_Label_N, BorderLayout.NORTH);
myButton1.add(myButton1_Label_E, BorderLayout.EAST);
myButton1.add(myButton1_Label_W, BorderLayout.WEST);
myButton1.add(myButton1_Label_S, BorderLayout.SOUTH);
frame = new JFrame();
frame.add(myButton1);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
FourLabelsInButton ggg = new FourLabelsInButton();
}
});
}
}
I thought of setting background images, but its not possible as the numbers are sums of dynamically generated numbers (the grid is dynamic).
The images for the buttons can also be generated dynamically.
You may also use a custom component. For this case the painting can be implemented quite straightforward:
class KakuroComponent extends JComponent {
private final int[] numbers;
public KakuroComponent(int... numbers) {
this.numbers = numbers;
}
#Override
public void paintComponent(Graphics g) {
int w = getWidth();
int h = getWidth();
g.setColor(Color.black);
g.fillRect(0, 0, w, h);
g.setColor(Color.white);
g.drawLine(0, 0, w, h);
g.drawLine(w - 1, 0, 0, h - 1);
if (numbers[0] > 0) // if there is a top number
drawStringCentered(g, String.valueOf(numbers[0]), w / 2, h / 6);
if (numbers[1] > 0) // if there is a left number
drawStringCentered(g, String.valueOf(numbers[1]), w / 6, h / 2);
if (numbers[2] > 0) // if there is a right number
drawStringCentered(g, String.valueOf(numbers[2]), w * 5 / 6, h / 2);
if (numbers[3] > 0) // if there is a bottom number
drawStringCentered(g, String.valueOf(numbers[3]), w / 2, h * 5 / 6);
}
void drawStringCentered(Graphics g, String s, int x, int y) {
Rectangle2D bounds = g.getFontMetrics().getStringBounds(s, g);
g.drawString(s, (int) (x - bounds.getCenterX()), (int) (y - bounds.getCenterY()));
}
}
There shouldn't be any reason you can't simply draw anything you want in the JButton by overwriting the paint() method.
public class KakuroSquare extends JButton
{
/* ... */
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
/* Your paint logic. */
}
}
I know the following code will move an object in a straight line. How can I get the object to travel in a wavy line? I know that something extra is required for the x variable.
public void draw(Graphics2D g)
{
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int)
(2 * r),
(int) (2 * r));
y++;
if (y - r > height)
y = -r;
}
Use the sine or cosine function to calculate y as a function of x.
Multiply the sine or cosine function to increase the amplitude (how high it goes)
y = 100 * sin(x) // will make it have peaks of -100 and 100
Divide the x to increase the period. (distance between peaks)
y = sin(x/2) // will make it take twice the x distance between peaks.
Something like this:
public void draw(Graphics2D g)
{
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int)
(2 * r),
(int) (2 * r));
x++; // Left to right movement
// Example, modify the multipliers as necessary
y = 100 * Math.sin(Math.toDegrees(x/4))
}
Including a sin(x) or cos(x) in your function will provide a regular wave pattern, irregular pattern needs a more sophisticated function
I know you already accepted an answer, but here's something to draw additional inspiration from that I whipped up...
package wavy;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Wavy {
public static void main(String[] args) {
final JFrame frame = new JFrame("Wavy!");
final WavyPanel wp = new WavyPanel();
frame.getContentPane().add(wp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Ticker t = new Ticker(wp);
final Repainter r = new Repainter(wp);
frame.pack();
frame.setVisible(true);
final Timer tickTimer = new Timer();
final Timer paintTimer = new Timer();
paintTimer.schedule(r, 1000, 50);
tickTimer.schedule(t, 1000, 10);
}
private static class WavyPanel extends JPanel {
private final Dimension size = new Dimension(640, 480);
private int amplitude = 50;
private int frequency = 5;
private int x = 0;
private double y = size.height / 2;
private int yBase = 0;
WavyPanel() {
super(true);
}
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, size.width, size.height);
g2.setColor(Color.BLACK);
g2.fillOval(x, (int)y, 30, 30);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
public Dimension getMinimumSize() {
return size;
}
#Override
public Dimension getMaximumSize() {
return size;
}
public void tick() {
//Move a pixel to the right; loop over to the left when reaching edge
x = (++x) % size.width;
//Length of one full wave = panel width divided by frequency
final int waveLength = size.width / frequency;
//Incrementing yBase; capping off at wavelength
yBase = (++yBase) % waveLength;
//Normalizing to [0..1]
final double normalized = (double)yBase / (double)waveLength;
//Full wave at 2*pi, means...
final double radians = normalized * Math.PI * 2;
//Getting the sine
final double sine = Math.sin(radians);
//Multiplying with amplitude, add to center position and we have our y
y = (int)(sine * amplitude) + size.height/2;
}
}
private static class Ticker extends TimerTask {
private final WavyPanel panel;
Ticker(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.tick();
}
}
private static class Repainter extends TimerTask {
private final WavyPanel panel;
Repainter(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.repaint();
}
}
}
This should run at an approximate 20 frames per second. You can increase this by setting the second argument of paintTimer.schedule(r, 1000, 50) lower. The speed of movement can be altered by lowering (speeding up) or increasing (slower) the second argument of tickTimer.schedule(t, 1000, 50).
Changing the amplitude field of WavyPanel will change how high/low the circle moves. Changing the frequency to a higher value will result in shorter waves, while a lower value will produce longer waves.
With some additional work you could add in controls to change the amplitude and frequency on-the-fly. Some additional notes:
You may wish to add some safeguard to the tick() method to make sure that when one invocation is already running, additional ones are skipped until the first one is done. Otherwise the calculations could fail for short tick intervals. A semaphore could be used here.
Since trigonometric calculations aren't exactly the cheapest, you may consider caching some results (e.g. in an array) for re-use if many similar animations are to be played or if there's a lot more drawing going on.
I hope I'm interpreting this right. Could use the sine or cosine of either your x or y coordinate. I'm not at a machine with java so I can't make an example at the moment..
You're right that you need to update both the x and y variables to get a wavy line. Here's the general strategy for a horizontal line that is wavy up and down:
Choose a function f(x) that has the shape you want. This will be used to calculate values for y. (For instance, you can use y = amplitude * Math.sin(frequency * x) to get a regular sine wave of a given amplitude and frequency.)
If necessary, write the code that implements your function.
Set x to some initial value.
In draw, before you paint the oval, calculate y = f(x);. Paint the oval and then increment x. If necessary, reset x so it stays in range.
If you want a vertical line that is wavy left and right, just reverse the roles of x and y in the above. If you want the oval to go in the reverse direction, just decrement instead of increment in step 4.
this sample is for point(Line with one length) on sinus graph and clock using.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class RunSwing extends JPanel {
static int x1 = 500;
static int y1 = 500;
static int x2 = x1;
static int y2 = y1;
final static int vectorLength = 100;
final static int sinx2 = x2;
final static int siny2 = y2;
static double count = 0;
private static RunSwing run = new RunSwing();
final Timer print = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
//increaseSinusGraph();
increaseClockVector();
count+=6; //for clock for 1 second
/*count++;//for sinus*/
if (count % 360 == 0)
System.out.println((count / 360) + " minute passed");
}
});
RunSwing() {
print.start();
}
public static void main(String[] args) {
JFrame frame = new JFrame("amir");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(run);
frame.setSize(1100, 700);
frame.setVisible(true);
}
static void increaseClockVector() {
double cos = Math.cos(Math.toRadians(count));
double sin = Math.sin(Math.toRadians(count));
y2 = siny2 + (int) (vectorLength * sin);
x2 = sinx2 + (int) (vectorLength * cos);
}
static void increaseSinusGraph() {
double sin = Math.sin(Math.toRadians(count));
y2 = siny2 + (int) (vectorLength * sin);
x2++;
}
private void createPoint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(x2, y2, x2 + 1, y2 + 1);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0));
g.drawLine(x1, y1, x2, y2);//for clock
/*g.drawLine(x2, y2, x2+1, y2+1);//for sinus*/
repaint();
}
}
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.