Related
My partner and I are trying to remake Tetris for our final project of the year in my Computer Science class we currently have a for loop that draws individual rectangles in an overwritten paint method.
private final int spacer = 30;
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
for(int i = getHeight()/2 - (spacer * 10); i < getHeight()/2 + (spacer * 10); i += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += (spacer)) {
g.drawRect(x, i, (spacer), (spacer));
}
}
setForeground(Color.black);
}
The method basically takes the width and height of the window and makes a 10 x 20 grid of boxes that are 30 units, pixels I think, wide.
We'd like to make a Grid.java class that takes in color, the spacer int, and an x and y int. The constructor for Grid.java should draw the exact same thing as the code above using the for loop, but when we tried it gave us a white screen that would not resize with the window.
private final int spacer = 30;
private static Grid[][] arr = new Grid[10][20];
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
int countY = 0;
int countX = 0;
for(int y = getHeight()/2 - (spacer * 10); y < getHeight()/2 + (spacer * 10); y += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += spacer) {
arr[countX][countY] = new Grid(x, y, spacer, g);
countX++;
}
countY++;
}
setForeground(Color.black);
}
*Grid.java Class*
package Tetris_Shapes;
import javax.swing.*;
import java.awt.*;
public class Grid {
private int x;
private int y;
private int side;
private Graphics g;
public Grid(int x, int y, int side, Graphics g) {
// g.drawRect(x, y, spacer, spacer);
this.x = x;
this.y = y;
this.side = side;
this.g = g;
paint(this.g);
}
private void paint(Graphics g) {
g.drawRect(x, y, side, side);
}
}
When we try and run this we get the white box that doesn't resize. My question is does anyone know of a way to get a constructor to draw shapes. Thank you in advance, this is pretty niche so I'm also going to apologize in advance.
Hi so i have here a 'falling' style game where i have homer simpsons head falling through the sky avoiding the salads and collecting burgers to increase score. I have implemented collision detection so when homer hits a salad he should re-spawn and have a life deducted which works fine. However if homers head hits the side of the salad and not directly in the centre of it the game will freeze for a moment and then carry on like nothing happened. I am not sure why this happens and wondering if there is a problem with how i am doing this. Here is my code below:
Are the collision detection not accurate enough or is there another issue that i am missing?
PImage background;
PImage MenuBackground;
int y=0;//global variable background location
final int End = 0;
final int Active = 1;
final int Menu = 2;
int gameMode = Menu;
int score = 0;
int lives = 3;
Boolean BurgerCollisionInProgress = false;
Boolean BurgerCollisionInProgress2 = false;
Salad salad1;
Salad salad2;
Salad salad3;
Homer user1;
Burger Burger;
public void settings()
{
size(500,1000); //setup size of canvas
}
void menu()
{
background = loadImage("spaceBackground.jpg"); //image used for background
background.resize(500,1000); //resizes the background
gameMode = Active;
float rand = random(25,475);
int intRand = int(rand);
float rand2 = random(25,475);
int intRand2 = int(rand2);
float rand3 = random(25,475);
int intRand3 = int(rand3);
float rand4 = random(25,475);
int intRand4 = int(rand4);
user1 = new Homer(250,100); //declares new defender as user1
Burger = new Burger(intRand,900,2);
salad1 = new Salad(intRand2,900,3);
salad2 = new Salad(intRand3,900,3);
salad3 = new Salad(intRand4,900,3); //3 aliens declared with their x and y position and their speed they move at
draw();
}
void setup()
{
if(gameMode == 2)
{
MenuBackground = loadImage("simpMenu.png");
MenuBackground.resize(540,1000);
image(MenuBackground, 0, y);
textAlign(CENTER);
textSize(40);
fill(252, 3, 3);
text("Press 'p' to play", 250,500);
}
}
void draw ()
{
if (gameMode == Active)
{
if(crash() == false)
{
drawBackground();//calls the drawBackground method
textSize(32);
fill(22,100,8);
text("Score: " + score,75,40);
text("Lives: " + lives,75,80);
salad1.update();//calls the update method which holds the move and render methods for alien
salad2.update();
salad3.update();
user1.render();//calls the update method which holds the move and render methods for user
Burger.update();//calls the update method which holds the move and render methods for burger
if(Bcrash() == true && BurgerCollisionInProgress == false)
{
score = score+1;
BurgerCollisionInProgress = true;
Burger.y = 900;
float rand = random(25,475);
int intRand = int(rand);
Burger.x = intRand;
}
if(Bcrash() == false)
{
BurgerCollisionInProgress = false;
}
if(crash() == true && BurgerCollisionInProgress2 == false)
{
if (lives < 1)
{ gameMode = End;
textSize(28);
fill(22,100,8);
text("Game Over, press 'r' to restart",200,200);
}
else
{
lives = lives - 1;
BurgerCollisionInProgress2 = true;
menu();
}
if(crash() == false)
{
BurgerCollisionInProgress2 = false;
}
}
}
}
}
void drawBackground()
{
image(background, 0, y); //draw background twice adjacent
image(background, 0, y-background.width);
y -=2;
if(y == -background.width)
y=0; //wrap background
}
boolean crash()
{
if(user1.crash(salad1))
{
return true;
}
if(user1.crash(salad2))
{
return true;
}
if(user1.crash(salad3))
{
return true;
}
return false;
}
boolean Bcrash()
{
if(user1.crash(Burger))
{
return true;
}
return false;
}
Homer class:
class Homer
{
PImage UserImage;
int x,y; //declaring variables
Homer(int x, int y)
{
this.x = x;
this.y = y;
UserImage = loadImage("homer.png");
UserImage.resize (60, 52);
} // end of Homer
void render()
{
//draw a Homer
image(UserImage,x,y);
} //end of void render
boolean crash(Salad A)
{
if((abs(x-A.x)<=30) && abs(y-A.y)<=30)
{
return true;
}
return false;
}// end of crash
boolean crash(Burger A)
{
if((abs(x-A.x)<=30) && abs(y-A.y)<=30)
{
return true;
}
return false;
}
} // end of class
Burger Class:
class Burger
{
PImage burgerImage;
int x,y, speedX;
int speedY = 0;
Burger(int x, int y, int speedY)
{
this.x = x;
this.y = y;
this.speedY= speedY;
burgerImage = loadImage("food.png");
burgerImage.resize (60, 52);
}
void render()
{
image(burgerImage,x,y);
}
void move()
{
y = y - speedY;
float rand = random(25,475);
int intRand = int(rand);
if(this.y < 0)
{
this.y = 900;
this.x = intRand;
}
}
void update()
{
move();
render();
}
}
Salad Class:
class Salad
{
float x,y;
float speedX, speedY; //declaring variables
PImage saladImage;
Salad(int x, int y, int speedY)
{
this.x = x;
this.y = y;
this.speedY = speedY;
saladImage = loadImage("salad.png");
saladImage.resize (60, 52);
} //end of salad
void move()
{
y=y-speedY;
float stepY = random(-5,5);
y = y + (int)stepY;
float rand = random(25,475);
int intRand = int(rand);
if(this.y < 0)
{
this.y = 900; // once the salads y is less than 0 they restart at 900
this.x = intRand;
speedY = speedY + 0.5;
}
} //end of void move
//draw a salad
void render()
{
image(saladImage,x,y);
} //end of void render
void update()
{
move();
render();
}
}// end of alien class
There are several little things which makes this harder than it could be. First, your intersect method isn't quite right. Then, the way you handle coordinates could be improved upon.
What I'm going to do first is to show you how to intersect rectangles. After that, I'll show you how I would deal with the drawable objets so they stay easy to manipulate. Then I'll show you some skeleton code for a short, easy game with stuff falling and colliding, and just for you I'll add some help so you can implement these suggestions into the context of your game.
1. Collisions
There are many ways to handle collisions. Most of them are applied mathematics, some of them are clever algorithms making use of colors or invisible sprites. There are probably methods I'm forgetting, too.
We'll only do collisions between rectangles, as your program seems quite rectangle-friendly and it's the easier method. So we'll write a intersection detection algorithm.
First thing to do when writing an algorithm is the pseudocode. I'm not joking. It's easy to go all clakety-clak with your keyboard and hit compile. It works most of the time... but it's more intuitive logic than applying your brain to the problem.
Being able to pseudocode is like a superpower for programmers. Never underestimate it.
Now, how do you know if two rectangles are intersecting? The answer is:
There are 4 ways that two rectangles can intersect, whether horizontally or vertically.
They must intersect both horizontally and vertically to overlap for real.
These are the possibilities you have to look for:
Red rectangle is bigger than black rectangle and black rectangle is completely inside it.
Both rectangles overlap on the left side (horizontally) or on the top side (vertically).
Red rectangle small enough to be inside black rectangle.
Both rectangles overlap on the right side (horizontally) or on the bottom side (vertically).
Because this code can be used in many places, I took it out of context and put it inside a fonction which takes coordinates and returns a boolean (true if there indeed is a collision):
// INTERSECT RECTs
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 && x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 && y1+h1 < y2+h2 || y1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
This is one way of handling collisions between rectangles. You could take this information and apply it to your game, and it would rock.
This said, you could also improve on your code with Inheritance...
2. Inheritance (in this case: for graphical objects)
Inheritance in computer science is a way to make a class obtain the properties of another one. Most people explains it in term of family: there is a parent class and there are children class which inherits the parent class' properties.
Inheritance is especially useful when several of your class share the same properties or methods. Drawable objects are a great example, because they all need coordinates. They all need a method to be drawn.
As you'll see with the example game later, I noticed that all my rectangles needed these modal variables:
protected float x, y, w, h; // x and y coordinate, width and height of the square
protected color fill, stroke;
protected float strokeWeight = 1;
So I created a base class named 'Drawable'. In a bigger project, it could be the base class of a whole tree of classes, like this:
So in this example, Rat would be the child of Walker, which is the child of Enemy, which is the child of Actor, which is the child of Drawable.
The advantage is that every child inherits everything from it's parent. It both makes you write less code and let you fix your mistakes in only one place instead of everywhere. For an example, if there's a mistake in how you use the coordinates of your objects, you want to fix it in the class where this logic is written, not in every class.
There are many other advantages to Inheritance, but for now let's keep it simple, all right?
3. Example program
This one is very straightforward: this is an example which use both inheritance and collisions. You can copy and paste it into a Processing IDE and it'll run. Take some time to see how the 3 classes relate to one another, and how every child class has the modal variables and functions of it's parent.
Hero hero;
ArrayList<Bomb> bombs = new ArrayList<Bomb>();
int numberOfBombs = 20; // if you change this number the number of bombs will change too. Try it!
int hitCount = 0;
public void settings()
{
size(800, 600); //setup size of canvas
}
public void setup() {
hero = new Hero();
for (int i = 0; i < numberOfBombs; i++) {
bombs.add(new Bomb(random(20, width-20), random(1, 10)));
}
// This part serves no purpose but to demonstrate that you can gather objets which share a parent class together
ArrayList<Drawable> myDrawables = new ArrayList<Drawable>();
for (Bomb b : bombs) {
myDrawables.add(b);
}
myDrawables.add(hero);
for (Drawable d : myDrawables) {
d.Render();
// Even though hero and the bombs are different classes, they are in the same ArrayList because they share the Drawable parent class.
// Drawable has the Render() function, which may be called, but the child class will overshadow the Drawable's method.
// Proof is that the error message "Drawable child: Render() was not overshadowed." will not appear in the console.
}
}
public void draw() {
DrawBackground();
hero.Update();
hero.Render();
for (Bomb b : bombs) {
b.Update();
b.Render();
}
ShowHitCount();
}
public void DrawBackground() {
fill(0);
stroke(0);
rect(0, 0, width, height, 0); // dark background
}
public void ShowHitCount() {
textAlign (RIGHT);
textSize(height/20);
fill(color(200, 200, 0));
text(hitCount, width-20, height/20 + 20);
}
class Drawable {
protected float x, y, w, h; // 'protected' is like 'private', but child class retain access
protected color fill, stroke;
protected float strokeWeight = 1;
Drawable() {
this(0, 0, 0, 0);
}
Drawable(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public void Render() { print("Drawable child: Render() was not overshadowed."); } // nothing to see here: this exists so we can overshadow it in the childs
public void Update() { print("Drawable child: Update() was not overshadowed."); } // same thing
}
class Hero extends Drawable { // 'extends' is keyword for 'will inherit from'
Hero() {
// 'super()' calls the parent's constructor
// in this example, I decided that the hero would be a red 40x60 rectangle that follows the mouse X position
super(mouseX - 20, height - 80, 40, 60);
fill = color(200, 0, 0);
stroke = color(250);
}
public void Update() { // when both parents and child have the same function (type and signature), the child's one prevail. That's overshadowing.
x = mouseX - w/2;
}
public void Render() {
fill(fill);
stroke(stroke);
strokeWeight(strokeWeight);
rect(x, y, w, h);
}
}
class Bomb extends Drawable {
protected float fallSpeed;
Bomb(float xPosition, float fallSpeed) {
// Bombs will be small blue squares falling from the sky
super(xPosition, -20, 20, 20);
this.fallSpeed = fallSpeed;
fill = color(0, 0, 200);
stroke = fill;
}
private void FallAgain() {
x = random(20, width-20);
fallSpeed = random(1, 10);
y = 0 - random(20, 100);
}
public void Update() {
y += fallSpeed;
// check for collision with the Hero
if (intersect(x, y, w, h, hero.x, hero.y, hero.w, hero.h)) {
hitCount++;
FallAgain();
}
// check if it fell lower than the screen
if (y > height) {
FallAgain();
}
}
public void Render() {
fill(fill);
stroke(stroke);
strokeWeight(strokeWeight);
rect(x, y, w, h);
}
}
// INTERSECT RECTs
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 && x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 && y1+h1 < y2+h2 || x1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
4. Bonus: help with implementation
So... you're seeing this and it makes you want to improve on your program. That's good. Maybe you want to implement some inheritance, maybe just the collisions. Both can be tricky, and neither is supposed to impact the user.
This is what is called 'refactoring'.
Let's implement a Drawable class first. The rest will be easier then.
First step: find what's the common ground with Burger, Homer and Salad. From the code you posted, I can see that they need these things:
int x, y;
int speedX, speedY;
PImage img;
// To which I would add:
int w, h;
boolean isVisible;
I notice that you're using integers. That's fine, but I strongly suggest using float for coordinates. I did the same thing when I was learning to code and I ended up regretting not using float earlier. Both integer and float will probably do the trick for this project (with some cast when needed).
Also, here are a couple functions that they share:
void Render()
void Update()
void Move()
// To which I would add:
void SetPosition()
void SetIsVisible()
boolean Crash() // so we can check if it intersect with given coordinates
So far, your Drawable class could look like this:
class Drawable {
public float x, y, w, h; // Making variables public while you could avoid it is bad practice, I'm doing it to avoid writing Get functions. Avoid doing this as much as possible, but bear with me for now.
protected float speedX, speedY;
protected PImage img;
protected boolean isVisible = true;
Drawable(float x, float y, float w, float h, String imagePath) {
this.x = x; // starting x position
this.y = y; // starting y position
this.w = w; // width if the object (your image in this case)
this.h = h; // height of the object (height of your image)
if (imagePath.length() > 0) { // if there is nothing in the string it won't try to load an image
img = loadImage(imagePath);
}
}
public void Render() {
if (isVisible && img != null) {
image(img, x, y);
}
}
public void Update() {
Move(); // I kept Move() out of Update() so you can overshadow Update() without having to re-code Move() later
}
protected void Move() {
// The 'normal' behavior of a Drawable would then to move according to it's speed.
// You can then change how they move by changing their speed values.
// Or... you can overshadow this function in a child class and write your own!
x += speedX;
y += speedY;
}
public void SetPosition(float x, float y) {
this.x = x;
this.y = y;
}
public void SetIsVisible(boolean isVisible) {
this.isVisible = isVisible;
}
public boolean Crash(float x, float y, float w, float h) {
// this function uses the 'intersect' function I wrote earlier, so it would have to be included in the project
return intersect(this.x, this.y, this.w, this.h, x, y, w, h);
}
}
Not so bad so far, isn't it? This will make a strong base for all your objects. Now, let's see how to implement this into your existing class:
Homer:
class Homer extends Drawable // give Homer the power of the Drawable class!
{
Homer(float x, float y)
{
// I can read in the code that your image will be (60, 52), but you have to write the manipulation here
super(x, y, 60, 52, "homer.png");
img.resize (60, 52);
}
public void Update() {
// do Update stuff so Homer can move around
}
}
Notice how smaller this class is now that all the Drawable stuff is dealt elsewhere.
Now, here's for the Salad class:
First, you can drop the salad1, salad2, salad3 global variables. We'll put them in a list, and you'll be able to have more or less of them if you want (you can think of this as being able to change the difficulty setting):
int numberOfSalads = 3;
ArrayList<Salad> salads = new ArrayList<Salad>();
In the place where you innitialize the salads, you can initialize them in a loop:
for (int i=0; i<numberOfSalads; i++) {
salads.add(new Salad(random(25,475), 900, 3);
}
Sure, there will be some modifications to make to the Salad class, too:
class Salad extends Drawable {
Salad(float x, float y, float speedY)
{
super(x, y, 60, 52, "salad.png");
this.speedY = speedY; // Drawable will take it from here
img.resize (60, 52);
}
protected void Move() // I knew this would come in handy!
{
// I have no idea what's going on, just re-writing your stuff
y = y - speedY;
y = y + random(-5, 5);
if (this.y < 0)
{
this.y = 900; // once the salads y is less than 0 they restart at 900
this.x = random(25, 475);
speedY = speedY + 0.5;
}
}
}
So far, so good. There are MANY other places where you'll have to adapt the code, but you should notice that so far you've removed more lines that You've added. That's a good thing. As long as your code is easy to read, making it shorter means that there's less places to look for nasty bugs to fix.
Also, when you avoid repeating the same lines (like all those identical Render functions) by having them all in one place (the Drawable class in this case), you also avoid having to hunt down every iteration of your code if you want to make one change. This is called DRY code. DRY (for Dont Repeat Yourself) code is waaay easier to debug and maintain. As a rule of thumb, every time you copy and paste code without any change, you should ask yourself if you could just keep these line in one centralized place, whether it's a variable or a function or a class.
I'll let you code the Burger class. I think you'll manage it now that you have seen how to deal with the others.
Now, let's take a look at how to update your main loop, draw():
void draw ()
{
// As a general rule, all your game states should be dealt in the game loop.
// I like 'switch' statements for this kind of operations
// Also, try not to clutter the game loop. If you have a lot of code here, you should probably put them into functions
// it will make it easier to read and the game loop can very easily become a spaghetti nightmare if you're not careful.
switch(gameMode) {
case Menu:
// Do Menu stuff
break;
case Active:
drawBackground(); // Maybe this should be before the switch, I'm not sure how you want to deal with this
// Updates
user1.Update();
burger.Update();
for (Salad s : salads) {
s.Update();
}
// Check for collisions
// I may be mistaken but I think only the Homer can collide with stuff
if (burger.Crash(user1.x, user1.y, user1.w, user1.h)) {
// Do burger crash stuff
}
for (Salad s : salads) {
if (s.Crash(user1.x, user1.y, user1.w, user1.h)) {
// Do Salad crash stuff
}
}
// Render
user1.Render();
burger.Render();
for (Salad s : salads) {
s.Render();
}
break;
case End:
// Do End stuff
break;
}
}
This should put you on track.
If, for some reason you only want to use the intersect method: remember that the width and height of your objects are the one you use for their images.
You probably have questions, don't hesitate to ask away. And have fun!
the function
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2,
float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 &&
x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 &&
y1+h1 < y2+h2 || y1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
only checks if rect1 is inside of rect2
in the function you dont neeed any or statements
here is the correct function
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float, w2, float h2)
{
boolean checkX = x1 < x2+w2 && x1+w1>x2;
boolean checkY = y1 < y2+h2 && y1+h1>y2;
return checkX && checkY;
}
I am trying to program a basic plotter using Applets. I need to use Applets specifically.
For the polt I have created a separate Canvas, but I have encountered a problem I cannot solve. When I draw any graph for the first time, it is drawn nicely. However, the canvas is not being repainted properly afterwards - I see in the debugging screen that the repaint() method was called and the paint() is invoked, but no graphics are updated.
Here is the code:
public class MyCanvas extends Canvas{
int w,h; //width and height
int samples;
ArrayList<Double> eqValues = new ArrayList<>();
MyCanvas(int wi, int he) //constructor
{ w=wi; h=he;
setSize(w,h); //determine size of canvas
samples=wi-20;
}
public void paint(Graphics g)
{
int y0=0, y1; //previous and new function value
g.setColor(Color.yellow);
g.fillRect(0,0,w,h); //clear canvas
g.setColor(Color.black);
if (eqValues.size()>0) { // draw new graph
for (int t = 1; t <= samples; t = t + 1) {
y1 = eqValues.get(t).intValue();
g.drawLine(10 + t - 1, h - y0, 10 + t, h - y1);
y0 = y1;
}
}
System.out.println("Repainted");
/*g.drawLine(10,10,10,h-10); //y-axis
g.drawLine(10,h/2,w-10,h/2); //x-axis
g.drawString("P",w-12,h/2+15);
g.drawString("P/2",w/2-13,h/2+15);
g.drawLine(w-10,h/2-2,w-10,h/2+2); //horizontal marks
g.drawLine(w/2, h/2-2,w/2, h/2+2);*/
}
public void drawSine(double amp, double xCoef, double phase){
for (int j=0;j<=samples;j++){
eqValues.add(amp*Math.sin(xCoef*Math.PI*j/samples + Math.PI*phase/180)+0.5+h/2);
}
repaint();
System.out.println("Got sine vals");
}
public void drawFOeq(double sc, double fc){
for (int j=0;j<=samples;j++){
eqValues.add(sc*j+fc);
}
repaint();
System.out.println("Got FO eq vals");
}
}
Thanks in advance!
The problem is when you add values to the ArrayList: you are putting them after the ones already in the ArrayList (with the add(Double) method). If you just want to clear the plot and draw a new function use the clear() method in the ArrayList of values before adding the new ones:
public void drawSine(double amp, double xCoef, double phase) {
eqValues.clear(); //this clear the ArrayList
......
repaint();
......
}
public void drawFOeq(double sc, double fc){
eqValues.clear(); //this clear the ArrayList
......
repaint();
......
}
If you want to plot multiple functions you have to create different ArrayList or, even better, store in the ArrayList all points (for example with java.awt.Point):
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
public class MyCanvas extends Canvas {
int w, h; // width and height
int samples;
ArrayList<Point> eqValues = new ArrayList<>(); //Point constructor receives 2 int arguments: x and y; however, his methods getX() and getY() return double values
// constructor
MyCanvas(int wi, int he) {
w = wi;
h = he;
setSize(w, h); // determine size of canvas
samples = wi - 20;
}
public void paint(Graphics g) {
int x1, y0, y1; // previous and new function value
g.setColor(Color.yellow);
g.fillRect(0, 0, w, h); // clear canvas
g.setColor(Color.black);
if (eqValues.size() > 0) { // draw new graph
y0 = (int) Math.round(eqValues.get(0).getY()); // first line must start at the first point, not at 0,h
for (Point p : eqValues) { // iterates over the ArrayList
x1 = (int) Math.round(p.getX());
y1 = (int) Math.round(p.getY());
g.drawLine(10 + x1 - 1, h - y0, 10 + x1, h - y1);
y0 = y1;
}
}
System.out.println("Repainted");
}
public void drawSine(double amp, double xCoef, double phase) {
for (int j = 0; j <= samples; j++) {
eqValues.add(new Point(j, (int) Math
.round(amp * Math.sin(xCoef * Math.PI * j / samples + Math.PI * phase / 180) + 0.5 + h / 2)));
}
repaint();
System.out.println("Got sine vals");
}
public void drawFOeq(double sc, double fc) {
for (int j = 0; j <= samples; j++) {
eqValues.add(new Point(j, (int) Math.round(sc * j + fc)));
}
repaint();
System.out.println("Got FO eq vals");
}
}
I have a problem.
I am a beginner with java, and succeeded up to this point. Add bubbles with random sizes.
Now I need to make the bubbles escaping mouse when he gets near them.
Can anyone give me a hint how?
Thank you.
public class BounceBall extends JFrame {
private ShapePanel drawPanel;
private Vector<NewBall> Balls;
private JTextField message;
// set up interface
public BounceBall() {
super("MultiThreading");
drawPanel = new ShapePanel(400, 345);
message = new JTextField();
message.setEditable(false);
Balls = new Vector<NewBall>();
add(drawPanel, BorderLayout.NORTH);
add(message, BorderLayout.SOUTH);
setSize(400, 400);
setVisible(true);
}
public static void main(String args[]) {
BounceBall application = new BounceBall();
application.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
private class NewBall extends Thread {
private Ellipse2D.Double thisBall;
private boolean ballStarted;
private int size, speed; // characteristics
private int deltax, deltay; // of the ball
public NewBall() {
ballStarted = true;
size = 10 + (int) (Math.random() * 60);
speed = 10 + (int) (Math.random() * 100);
int startx = (int) (Math.random() * 300);
int starty = (int) (Math.random() * 300);
deltax = -10 + (int) (Math.random() * 21);
deltay = -10 + (int) (Math.random() * 21);
if ((deltax == 0) && (deltay == 0)) {
deltax = 1;
}
thisBall = new Ellipse2D.Double(startx, starty, size, size);
}
public void draw(Graphics2D g2d) {
if (thisBall != null) {
g2d.setColor(Color.BLUE);
g2d.fill(thisBall);
}
}
public void run() {
while (ballStarted) // Keeps ball moving
{
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
System.out.println("Woke up prematurely");
}
// calculate new position and move ball
int oldx = (int) thisBall.getX();
int oldy = (int) thisBall.getY();
int newx = oldx + deltax;
if (newx + size > drawPanel.getWidth() || newx < 0) {
deltax = -deltax;
}
int newy = oldy + deltay;
if (newy + size > drawPanel.getHeight() || newy < 0) {
deltay = -deltay;
}
thisBall.setFrame(newx, newy, size, size);
drawPanel.repaint();
}
}
}
private class ShapePanel extends JPanel {
private int prefwid, prefht;
public ShapePanel(int pwid, int pht) {
prefwid = pwid;
prefht = pht;
// add ball when mouse is clicked
addMouseListener(
new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
NewBall nextBall = new NewBall();
Balls.addElement(nextBall);
nextBall.start();
message.setText("Number of Balls: " + Balls.size());
}
});
}
public Dimension getPreferredSize() {
return new Dimension(prefwid, prefht);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < Balls.size(); i++) {
(Balls.elementAt(i)).draw(g2d);
}
}
}
}
You should not have a Thread for each individual ball, this will not scale well, the more balls you add, the more threads you add. At some point, the amount of work it takes to manage the threads will exceed the benefit for using multiple threads...
Also, I doubt if your need 1000fps...something like 25fps should be more than sufficient for your simple purposes. This will give the system some breathing room and allow other threads within the system time to execute.
Lets start with a simple concept of a Ball. The Ball knows where it is and which direction it is moving it, it also knows how to paint itself, for example...
public class Ball {
private int x;
private int y;
private int deltaX;
private int deltaY;
private int dimeter;
private Ellipse2D ball;
private Color color;
public Ball(Color color, Dimension bounds) {
this.color = color;
Random rnd = new Random();
dimeter = 5 + rnd.nextInt(15);
x = rnd.nextInt(bounds.width - dimeter);
y = rnd.nextInt(bounds.height - dimeter);
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
int maxSpeed = 10;
do {
deltaX = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
} while (deltaX == 0);
do {
deltaY = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
} while (deltaY == 0);
ball = new Ellipse2D.Float(0, 0, dimeter, dimeter);
}
public void update(Dimension bounds) {
x += deltaX;
y += deltaY;
if (x < 0) {
x = 0;
deltaX *= -1;
} else if (x + dimeter > bounds.width) {
x = bounds.width - dimeter;
deltaX *= -1;
}
if (y < 0) {
y = 0;
deltaY *= -1;
} else if (y + dimeter > bounds.height) {
y = bounds.height - dimeter;
deltaY *= -1;
}
}
public void paint(Graphics2D g2d) {
g2d.translate(x, y);
g2d.setColor(color);
g2d.fill(ball);
g2d.translate(-x, -y);
}
}
Next, we need somewhere for the balls to move within, some kind of BallPit for example...
public class BallPit extends JPanel {
private List<Ball> balls;
public BallPit() {
balls = new ArrayList<>(25);
balls.add(new Ball(Color.RED, getPreferredSize()));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update(getSize());
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
This maintains a list of balls, tells them when the need to update and when the need to paint. This example uses a simple javax.swing.Timer, which acts as the central timer which updates the balls and schedules the repaints.
The reason for this is takes care of synchronisation between the updates and the paints, meaning that the balls won't be updating while they are been painted. This is achieved because javax.swing.Timer triggers it's callbacks within the context of the EDT.
See Concurrency in Swing and How to use Swing Timers for more details.
Okay, so that fixes the threading issues, but what about the mouse avoidance...
That's a little more complicated...
What we need to is add a MouseMoitionListener to the BillPit and record the last position of the mouse.
public class BallPit extends JPanel {
//...
private Point mousePoint;
//...
public BallPit() {
//...
MouseAdapter handler = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
}
#Override
public void mouseExited(MouseEvent e) {
mousePoint = null;
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
//...
The reason for including mouseExit is to ensure that balls don't try and move away from a phantom mouse cursor...
Next, we need to update Ball to have an "area of effect", this is the area around the ball that will trigger a change in movement if the mouse cursor moves within it's range...
public class Ball {
//...
private final Ellipse2D.Float areaOfEffect;
public Ball(Color color, Dimension bounds) {
//...
areaOfEffect = new Ellipse2D.Float(-10, -10, dimeter + 20, dimeter + 20);
}
Now, I also add some additional painting for debug reasons...
public void paint(Graphics2D g2d) {
g2d.translate(x, y);
g2d.setColor(new Color(0, 0, 192, 32));
g2d.fill(areaOfEffect);
g2d.setColor(color);
g2d.fill(ball);
g2d.translate(-x, -y);
}
Next, we need to modify the Ball's update method to accept the mousePoint value...
public void update(Dimension bounds, Point mousePoint) {
PathIterator pathIterator = areaOfEffect.getPathIterator(AffineTransform.getTranslateInstance(x, y));
GeneralPath path = new GeneralPath();
path.append(pathIterator, true);
if (mousePoint != null && path.contains(mousePoint)) {
// Determine which axis is closes to the cursor...
int xDistance = Math.abs(x + (dimeter / 2) - mousePoint.x);
int yDistance = Math.abs(y + (dimeter / 2) - mousePoint.y);
if (xDistance < yDistance) {
// If x is closer, the change the delatX
if (x + (dimeter / 2) < mousePoint.x) {
if (deltaX > 0) {
deltaX *= -1;
}
} else {
if (deltaX > 0) {
deltaX *= -1;
}
}
} else {
// If y is closer, the change the deltaY
if (y + (dimeter / 2) < mousePoint.y) {
if (deltaY > 0) {
deltaY *= -1;
}
} else {
if (deltaY > 0) {
deltaY *= -1;
}
}
}
}
//...Rest of previous method code...
}
Basically, what this is trying to do is determine which axis is closer to the mouse point and in which direction the ball should try and move...it's a little "basic", but gives the basic premise...
Lastly, we need to update the "update" loop in the javax.swing.Timer to supply the additional parameter
for (Ball ball : balls) {
ball.update(getSize(), mousePoint);
}
I'm going to answer this, but I'm very close to issuing a close vote because it doesn't show what you've done so far to attempt this. I would not be surprised if others are closer to the edge than I am on this. At the same time, you've clearly shown your progress before you reached this point, so I'll give you the benefit of the doubt. In the future, I would strongly advise making an attempt and then posting a question that pertains to the specific problem you're having while making that attempt.
You need two things:
The current location of the mouse
A range check and reversal of direction if too close.
The location of the mouse can be achieved by adding two variables (x and y) and, every time the mouse is moved (so add a mouse event listener to your JPanel or something) update those variables with the new location.
Then, you can do a range check (think Pythagorean theorem) on each bubble to make sure they're far enough away. If the bubble is too close, you'll want to check where that bubble would end up if it carried on its current course, as well as where it would end up if it changed X direction, Y direction, or both. Pick the one that ends up being furthest away and set the deltax and deltay to those, and let the calculation carry on as normal.
It sounds like a lot, but those are the two basic components you need to achieve this.
I'm trying to utilize swing graphics in order to gauge Bresenham's algorithm against a less polished solution (I haven't implemented the timers yet). As things stand, there are no errors when compiling, and it throws a NullPointer exception at basic, drawthoselines, and main. The idea is that the lines will appear in the JFrame, but they don't. It's just a blank frame. I know I have everything set to static, but I get a lot of errors otherwise.
I'm a novice and I would be grateful to anyone who could provide a solution and an explanation.
import java.awt.*;
import javax.swing.*;
public class lines extends JPanel {
static int deltaX;
static int deltaY;
static int DY2;
static int DX2;
static int Di;
public static void main (String[] args) {
JFrame f = new JFrame("Line vs Line");
f.pack();
f.setVisible(true);
f.setSize(300,300);
f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
f.getContentPane().add(p);
Graphics g = null;
drawthoselines(g);
}
public static void basic(int x1, int y1, int x2, int y2, Graphics g){
int deltaX = x2-x1;
int deltaY = y2-y1;
float m = (float)deltaY/(float)deltaX;
float c = y1 - (m*x1);
for (int x=x1; x<x2; x++){
float floatY = (m*x) + c;
int y = Math.round(floatY);
g.drawLine(x,y,x,y);
}
}
public static void brz(int x1, int y1, int x2, int y2, Graphics g){
deltaX = x2-x1;
deltaY = y2-y1;
DY2 = 2* deltaY;
DX2 = 2* deltaX;
Di = DY2 - deltaX;
int x = x1;
int y = y1;
int prevy;
while (x<x2) {
x++;
prevy = y;
if (Di > 0){
y++;
}
g.drawLine(x,y,x,y);
Di = Di + DY2 - (DX2 * (y - prevy));
}
}
public static void drawthoselines(Graphics g){
basic(10,10,40,30,g);
basic(10,10,40,90,g);
brz(50,50,150,60,g);
brz(50,50,150,120,g);
brz(50,50,150,140,g);
}
}
That is not the way you do custom painting. Read the Swing tutorial on Custom Painting for explanations on how painting works and for working examples.
Also, whenever you see all static variables and method you know you are doing something else wrong. I suggest you take time to read other section of the tutorial as well since they all contain examples on a better way to structure your code.
You don't instantiate Graphics because it is passed down from the java.awt.event
Also you have a class that extends JPanel, which means you want to add the class to the JFrame by instantiating the class. Also, the class will implicitly call paintComponent method which you will override to make use of the Graphics g. It's a lot of stuff to take in so go slowly (start from rudimentary examples).
Let me also inform you a bit on static modifier.
Static modifier runs when you load the class. Thus if a method that is not a static is in a static method, the method will need to be called by instantiating the object that holds the method. Because you need the object (class) loaded to be able to use the method.
Below should work:
import java.awt.;
import javax.swing.;
public class lines extends JPanel {
static int deltaX;
static int deltaY;
static int DY2;
static int DX2;
static int Di;
public static void main (String[] args) {
JFrame f = new JFrame("Line vs Line");
f.pack();
f.setVisible(true);
f.setSize(300,300);
f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
lines h = new lines();
f.getContentPane().add(h);
}
public static void basic(int x1, int y1, int x2, int y2, Graphics g){
int deltaX = x2-x1;
int deltaY = y2-y1;
float m = (float)deltaY/(float)deltaX;
float c = y1 - (m*x1);
for (int x=x1; x<x2; x++){
float floatY = (m*x) + c;
int y = Math.round(floatY);
g.drawLine(x,y,x,y);
}
}
public static void brz(int x1, int y1, int x2, int y2, Graphics g){
deltaX = x2-x1;
deltaY = y2-y1;
DY2 = 2* deltaY;
DX2 = 2* deltaX;
Di = DY2 - deltaX;
int x = x1;
int y = y1;
int prevy;
while (x<x2) {
x++;
prevy = y;
if (Di > 0){
y++;
}
g.drawLine(x,y,x,y);
Di = Di + DY2 - (DX2 * (y - prevy));
}
}
public static void drawthoselines(Graphics g){
}
#Override
protected void paintComponent(Graphics g) {
basic(10,10,40,30,g);
basic(10,10,40,90,g);
brz(50,50,150,60,g);
brz(50,50,150,120,g);
brz(50,50,150,140,g);
}
}