I am working on a dusk cleaner simulator on Java, in this case the shape of the cleaner is a circular ball.
The program is quite simple, the user puts in the width and length of the "room" and coordinates x and y.
What I want to do and cannot is create a series of commands, each represented by a character. There are three commands that I want to imnplement:
1. char 'A' = Move forward 1 meter
2. char 'L' = Turn left 90 degrees
3. R Turn right 90 degrees
Example of user input AALA, in this case the expected output is that the machine moves 2 meters and then turns left 90 degrees and then moves 1 meter again. Hope I am clear.
As you can see in the code, I have tried to create an array of chars but I dont know what the next step should be...
The code:
public class Cleaner extends JPanel {
/* int lx = 1, ly = 1;
int x = 200, y = 250;
*/
int x, y;
int width = 52, height = 50; // width and height of the "dust sucker"
int lx , ly;
// an array of chars
char[] charArray ={ 'A', 'L', 'R'};
java.util.Timer move; // making the instance of Timer class from the util package
static JFrame frame;
Cleaner()
{
frame = new JFrame ("Cleaner started!"); // passing attributes to our fame
frame.setSize (400, 400); // setting size of the starting window
frame.setVisible (true);
setForeground(Color.black); // setting color
move = new java.util.Timer();
move.scheduleAtFixedRate(new TimerTask()
{
public void run()
{
if(x<0)
lx = 1;
if(x>=getWidth()-45)
lx = -1; // -1 sets boundry for the dusk sucker
if(y<0)
ly = 1;
if(y>=getHeight()-45)
ly = -1; // -1 sets boundry for the dusk sucker
x+=lx; // to make the machine move
y+=ly;
repaint();
}
}, 0, 5// speed of the machine
);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint (Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x, y, width, height);
}
public static void main (String[] args)
{
// lx value
String lxValue =
JOptionPane.showInputDialog( "Enter lx" );
// ly value
String lyValue =
JOptionPane.showInputDialog( "Enter ly" );
String xValue =
JOptionPane.showInputDialog( "Enter x value" );
// ly value
String yValue =
JOptionPane.showInputDialog( "Enter y value" );
// convert String inputs to int values
int firstInput = Integer.parseInt( lxValue );
int secondInput = Integer.parseInt( lyValue );
int thirdInput = Integer.parseInt( xValue );
int forthInput = Integer.parseInt( yValue );
Cleaner cleaner = new Cleaner();
frame.add(cleaner);
cleaner.lx = firstInput;
cleaner.ly = secondInput;
cleaner.x = thirdInput;
cleaner.y = forthInput;
}
}
All help is appreciated!
First some basics:
override paintComponent(), not paint() when doing custom painting.
use a Swing Timer for animation. All updates to Swing components need to be done on the Event Dispatch Thread (EDT).
in this case the shape of the cleaner is a circular ball.
So you need to create a class to represent the ball. It will have basic properties like:
size
location
direction of movement.
speed of movement.
You will then need to create methods to change the properties. Maybe:
move() - move in the current direction at the current speed
turnRight() - adjust direction
turnLeft() - adjust direction
Then you can create an ArrayList to store the moves and a Swing Timer to execute the moves.
Whenever the Timer fires you remove the command from the ArrayList and execute the command. By invoking one of the above 3 methods.
Check out: get width and height of JPanel outside of the class for an example that is similiar (not exact) to what you want. It demonstrates the concept if creating an object with the properties needed to control its motion.
Related
I have been working on a Java project for Uni, the classic arcade game Breakout, and so far have managed to create the bat and ball objects and they work as intended. I'd like to implement the brick wall using an array as making each brick its own object will result in inefficient code, but my experience with Java doesn't extend to Arrays and I understand, unlike Python, they are tricky to get working.
I'd like the bricks to be given different positions based on x and y parameters already established.
Here is the Model class where I'd like to add the array;
public class Model
{
// First, a collection of useful values for calculating sizes and layouts etc.
public int B = 6; // Border round the edge of the panel
public int M = 40; // Height of menu bar space at the top
public int BALL_SIZE = 30; // Ball side
public int BRICK_WIDTH = 50; // Brick size
public int BRICK_HEIGHT = 30;
public int BAT_MOVE = 5; // Distance to move bat on each keypress
public int BALL_MOVE = 3; // Units to move the ball on each step
public int HIT_BRICK = 50; // Score for hitting a brick
public int HIT_BOTTOM = -200; // Score (penalty) for hitting the bottom of the screen
View view;
Controller controller;
public GameObj ball; // The ball
public ArrayList<GameObj> bricks; // The bricks
public GameObj bat; // The bat
public int score = 0; // The score
// variables that control the game
public boolean gameRunning = true; // Set false to stop the game
public boolean fast = false; // Set true to make the ball go faster
// initialisation parameters for the model
public int width; // Width of game
public int height; // Height of game
// CONSTRUCTOR - needs to know how big the window will be
public Model( int w, int h )
{
Debug.trace("Model::<constructor>");
width = w;
height = h;
}
// Initialise the game - reset the score and create the game objects
public void initialiseGame()
{
score = 0;
ball = new GameObj(width/2, height/2, BALL_SIZE, BALL_SIZE, Color.RED );
bat = new GameObj(width/2, height - BRICK_HEIGHT*3/2, BRICK_WIDTH*3,
BRICK_HEIGHT/4, Color.GRAY);
bricks = new ArrayList<>();
// ***HERE***
}
And here is the corresponding code I'd like added to View class to draw the bricks in the GUI;
public void drawPicture()
{
// the ball movement is runnng 'i the background' so we have
// add the following line to make sure
synchronized( Model.class ) // Make thread safe (because the bal
{
GraphicsContext gc = canvas.getGraphicsContext2D();
// clear the canvas to redraw
gc.setFill( Color.WHITE );
gc.fillRect( 0, 0, width, height );
// update score
infoText.setText("BreakOut: Score = " + score);
// draw the bat and ball
displayGameObj( gc, ball ); // Display the Ball
displayGameObj( gc, bat ); // Display the Bat
// ***HERE***
}
}
The .jar project file in its current state can be viewed here.
On a side note, there is a slight bug with the bat, it does not stop when it hits either side, not sure what's the best way to go about making it stay within the parameters of the window.
Thanks in advance!!
You already have your ArrayList of bricks. Create a function to convert a row/column co-ordinate into an index for your array. Say you have a grid 3x4 (3 rows, 4 columns):
0 |_|_|B|_|
1 |_|_|_|_|
2 |_|_|_|_|
0 1 2 3
Your ArrayList would be of size 12 (3x4).
private int translateCoordToIndex(int row, int col) {
return row * TOTAL_COLS + col
}
So for the brick at row=0,col=2 in the diagram its position comes out as (0*4)+2 = 2 which means index 2 in your ArrayList bricks.get(2)
You can even just change the ArrayList into a general array of GameObj GameObj[]
Alternatively you can use a 2d array of GameObj to represent a grid of bricks.
GameObj[][] bricks = new GameObj[rows][cols] This way you can access the exact brick using its row and column position - e.g. bricks[0][0] for the first brick.
Both approaches are pretty much the same.
I have been doing a small little project using Processing, and the effect I wanted to achieve was a kind of "mountains" forming and moving, using Perlin Noise with the noise() function, with 2 parameters.
I was originally using a image for the background, but for illustrational purposes, I made the background black, and it's basically the same effect.
My issue is that I want to have a "history" of the mountains because they should fade away after some time, and so I made a history of PShapes, and draw the history and update it each frame.
Updating it is no issue, but drawing the PShapes seems to take a lot of time, reducing the frame rate from 60 to 10 when the length of the history is 100 elements.
Below is the code I used :
float noise_y = 0;
float noise_increment = 0.01;
// increment x in the loop by this amount instead of 1
// makes the drawing faster, since the PShapes have less vertices
// however, mountains look sharper, not as smooth
// bigger inc = better fps
final int xInc = 1;
// maximum length of the array
// bigger = less frames :(
final int arrLen = 100;
int lastIndex = 0;
PShape[] history = new PShape[arrLen];
boolean full = false;
// use this to add shapes in the history
PShape aux;
void setup() {
size(1280, 720);
}
void draw() {
background(0);
// create PShape object
aux = createShape();
aux.beginShape();
aux.noFill();
aux.stroke(255);
aux.strokeWeight(0.5);
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
history[lastIndex++] = aux;
// if it reached the maximum length, start it over ( kinda works like a queue )
if (lastIndex == arrLen) {
lastIndex = 0;
full = true;
}
// draw the history
// this part takes the MOST TIME to draw, need to fix it.
// without it is running at 60 FPS, with it goes as low as 10 FPS
if (full) {
for (int i = 0; i < arrLen; i++) {
shape(history[i]);
}
} else {
for (int i = 0; i < lastIndex; i++) {
shape(history[i]);
}
}
noise_y = noise_y - noise_increment;
println(frameRate);
}
I have tried to use different ways of rendering the "mountains" : I tried writing my own class of a curve and draw lines that link the points, but I get the same performance. I tried grouping the PShapes into a PShape group object like
PShape p = new PShape(GROUP);
p.addChild(someShape);
and I got the same performance.
I was thinking of using multiple threads to render each shape individually, but after doing some research, there's only one thread that is responsible with rendering - the Animation Thread, so that won't do me any good, either.
I really want to finish this, it seems really simple but I can't figure it out.
One possible solution would be, not to draw all the generated shapes, but to draw only the new shape.
To "see" the shapes of the previous frames, the scene can't be cleared at the begin of the frame, of course.
Since the scene is never cleared, this would cause, that the entire view is covered, by shapes over time. But if the scene would be slightly faded out at the begin of a new frame, instead of clearing it, then the "older" shapes would get darker and darker by time. This gives a feeling as the "older" frames would drift away into the depth by time.
Clear the background at the initlization:
void setup() {
size(1280, 720);
background(0);
}
Create the scene with the fade effect:
void draw() {
// "fade" the entire view
blendMode(DIFFERENCE);
fill(1, 1, 1, 255);
rect(0, 0, width, height);
blendMode(ADD);
// create PShape object
aux = createShape();
aux.beginShape();
aux.stroke(255);
aux.strokeWeight(0.5);
aux.noFill();
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
int currentIndex = lastIndex;
history[lastIndex++] = aux;
if (lastIndex == arrLen)
lastIndex = 0;
// draw the newes shape
shape(history[currentIndex]);
noise_y = noise_y - noise_increment;
println(frameRate, full ? arrLen : lastIndex);
}
See the preview:
I am learning processing right now and I am trying to make a sketch that could change colour when the sound changes.
(When Amplitude + ,
Then Brightness+ )
Because changing colour does not need to change as rapid as the draw() function. So how could I build a clock so that the color would not change in every draw?
This is the code I am using right now:
import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
Minim minim;
AudioPlayer song;
FFT fft;
BeatDetect beat;
color start=color(0,0,0);
color finish;
float amt = 0.0;
void setup()
{
frameRate(50);
size(600,600,P3D);
minim = new Minim(this);
song = minim.loadFile("song2.mp3", 512);
song.loop();
fft = new FFT(song.bufferSize(), song.sampleRate());
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
}
// draw is run many times
void draw()
{
float brightness = map( 0, 0, song.bufferSize(), 0, 255 );
background( brightness );
// println(song.bufferSize());
stroke(100);
// draw the waveforms
for( int i = 0; i < song.bufferSize() - 1; i++ )
{
// find the x position of each buffer value
float x1 = map( i, 0, song.bufferSize(), 0, width );
float x2 = map( i+1, 0, song.bufferSize(), 0, width );
// draw a line from one buffer position to the next for both channels
line( x1, 50 + song.left.get(i)*50, x2, 50 + song.left.get(i+1)*50);
line( x1, 150 + song.right.get(i)*50, x2, 150 + song.right.get(i+1)*50);
println(x1);
}
}
When you call frameRate(50); you are telling Processing to (try to) update the draw() 50 times a second. You can tell how many frames have passed since the start of the sketch by checking the built-in variable frameCount.
This can then be divided by a number which represents how many frames you want to draw before doing something special - I would use modulus for this, it will divide the numbers and return the remainder. If it equals 0, then that number of frames have passed.
int updateTriggerCount = 10;
void setup() {
...
}
void draw()
{
if((frameCount % updateTriggerCount) == 0)
{
// Another 10 frames have passed! Do something special
}
....
}
Trusting in frameRate to control timing is ok, but is, of course, frameRate dependent. Meaning that if your frameRate drops the timimg will drop together.
To avoid that, you may use millis() and attach your timing to, well time :)
Here a very simple timer example:
PFont font;
String time = "000";
int initialTime;
int interval = 1000;//one second
color bg = color (255);
void setup()
{
size(300, 300);
font = createFont("Arial", 30);
background(255);
fill(0);
initialTime = millis();
frameRate(30);// changed framerate to exemplify
}
void draw()
{
background(bg);
if (millis() - initialTime > interval)
{
time = nf(int(millis()/1000), 3);
initialTime = millis();
bg = color (random(255), random(100), random(255));
}
text(time, width/2, height/2);
}
I am working on creating a game for fun that basically is a simplistic representation for evolution.
Essentially, when I click on my moving ball it changes color. The goal is to continuously change until it matches the background color meaning the ball is successfully hidden. Eventually I will add more balls but I am trying to figure out how to change its color upon a mouse click. I have created the moving ball animation so far.
How can I change the ball color when I click on the ball?
Code:
public class EvolutionColor
{
public static void main( String args[] )
{
JFrame frame = new JFrame( "Bouncing Ball" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
BallPanel bp = new BallPanel();
frame.add( bp );
frame.setSize( 1800, 1100 ); // set frame size
frame.setVisible( true ); // display frame
bp.setBackground(Color.YELLOW);
} // end main
}
class BallPanel extends JPanel implements ActionListener
{
private int delay = 10;
protected Timer timer;
private int x = 0; // x position
private int y = 0; // y position
private int radius = 15; // ball radius
private int dx = 2; // increment amount (x coord)
private int dy = 2; // increment amount (y coord)
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e)
// will run when the timer fires
{
repaint();
}
public void mouseClicked(MouseEvent arg0)
{
System.out.println("here was a click ! ");
}
// draw rectangles and arcs
public void paintComponent( Graphics g )
{
super.paintComponent( g ); // call superclass's paintComponent
g.setColor(Color.red);
// check for boundaries
if (x < radius) dx = Math.abs(dx);
if (x > getWidth() - radius) dx = -Math.abs(dx);
if (y < radius) dy = Math.abs(dy);
if (y > getHeight() - radius) dy = -Math.abs(dy);
// adjust ball position
x += dx;
y += dy;
g.fillOval(x - radius, y - radius, radius*2, radius*2);
}
}
Take a look at How to Write a Mouse Listener.
Don't make decisions about the state of the view in the paintComponent, painting can occur for any number of reasons, many you don't control. Instead, make theses decisions within the actionPerformed method of your Timer
You may also wish to consider changing your design slightly. Rather then having the balls as JPanels, you create a virtual concept of a ball, which contains all the properties and logic it needs and use the JPanel to paint them. You could then store them in some kind of List, each time you register a mouse click you could iterate the List and check to see if any of the balls were clicked
Have look at Java Bouncing Ball for an example
Instead of hard-coding the color (g.setColor(Color.red);), why not create an attribute:
g.setColor(currentColor);
Then when you click in the circle area, change currentColor.
I am trying to diffuse a color in java Draw (which doesn't have the capability to diffuse normally) but I ran into an error and I can't seem to spot it.
The way I went about diffusing was by writing a small script that would draw my shape hundreds of times, each time smaller and slightly varying in color. This is my snippet:
import javax.swing.*;
import java.awt.*;
public class DiffuseDraw extends JFrame
{
//set size of window here
final int length = 350;
final int height = 370;
public DiffuseDraw()
{
super("Graphics Project Window");
Container container = getContentPane();
setBackground(new Color(0,0,0));
setSize(length, height);
setVisible(true);
}
// my problem comes here:
public void paint(Graphics g)
{
Draw.fillRectangle(g,0,0,length,height,new Color(19,24,32));
int rad = 325; // size of the biggest circle
float floatGreen = 0; // Color components for black
float floatBlue = 0;
float floatRed = 0;
int counter = 0; // the counter
while (counter < 290) {
rad = rad - 1; // Here we shrink the by a small incriment
floatRed = floatRed + 87/290; //red component slowly goes brighter
floatBlue = floatBlue + 178/290; // blue component slowly goes brighter
floatGreen = floatGreen + 211/290; // green component slowly goes brighter
int intRed = (int)Math.round(floatRed);
int intBlue = (int)Math.round(floatBlue);
int intGreen = (int)Math.round(floatGreen);
Draw.fillCircle(g,173,307,rad,new Color(intRed,intBlue,intGreen));
counter = counter + 1;
}
}
public static void main(String args[]) {
DiffuseDraw prog = new DiffuseDraw();
prog.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
When I compile and run, I only get a black screen. I am guessing that the problem is coming from the colors not changing. The number I am adding the float red blue and green by came from me wanting to diffuse a type of blue, so I took the the brightest color of blue that would appear and divided it by 290, the number of times the loop would run.
Instead of
87/290, 178/290, and 211/290,
try using
(float)87/290, (float)178/290, and (float)211/290.
That will, at least, add some color to the window- the problem is that by default, a number like 87/290 will be evaluated to 0; casting it to a float will give the desired result of 0.3 instead.