I'm writing a tank game . I want to have a method called shoot that when I press Space the tank have to shoot . my problem is that when the program calls this method it goes through the while loop and after that it prints the end location of the ball . I need to implement something in the while loop that every time it calculates dx and dy it goes to the paint method and paint the new location of the ball. I tried adding paintImmediately() but it throws stackoverflow error. thanks for helping me.
actually I'm changing dx and dy and I want the paint method to draw the ball at that place...
public void shoot(Image img, double fromx, double fromy, double ydestination, int speed) {
int time = 0;
double speedy, speedx;
while (dy!=ydestination) {
time++;
speedy = speed * Math.sin(Math.toRadians(angle));
speedx = speed * Math.cos(Math.toRadians(angle));
dy = (int) ((-5) * time * time + speedy * time + fromy);
dx = (int) (speedx * time + fromx);
// paintImmediately((int)dx,(int) dy, 10, 10);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
and here is my overrided paint method the last line is for the bullet that is my question :
#Override
public void paint(Graphics g) {
System.out.println("paint");
super.paint(g);
render(bufferedGraphics);
g.drawImage(bufferedScreen, 0, 0, null);
// System.out.println(x1);
BufferedImage buff = rotateImage(mile1, angle);
BufferedImage buf = rotateImage(mile2, angle);
g.drawImage(buff, mx1 - 40, my1, null);
g.drawImage(buf, mx2 , my2, null);
g.drawImage(bullet, (int) dx, (int) dy, null);
//setVisible(true);
}
You are using the wrong approach. You are tying 3 events together: user input (click to shoot), game state update (bullet moves) and draw refresh rate (paint).
In general trying to make these work at the same speed is a nightmare and you'll never achieve it. The most common, easy and robust approach is to have an event loop. User input events trigger changes to the game state, the game state is updated periodically either by turns or by some elapsed time (and state update will depend on how much time has elapsed), the state is drawn every time it is needed, which is periodically but also for some other events like minimizing the windows, etc etc...
For Java, you can find a good library for this here. With a sample hello world that shows the different parts here.
P.S: Also, be very careful when manually sending threads to sleep. That might make your entire program unresponsive.
If your drawing code has sleep() in it to introduce waits, you are doing things very wrong indeed. You are trying to sleep there because you want the screen to keep on updating with new positions... but you are actually making the screen freeze completely, because there is only 1 thread drawing things in Java Swing; and if you sleep that thread, nothing gets drawn (and you can't even press keys or use the mouse).
What you should do instead is to update the position of your bullet over several calls to your paint() method. In pseudocode:
paint(Graphics g) {
// calls paint() on all objects in the game world
}
// you should call this once per frame
update(World w) {
// call update() on each game-world object
}
// in Tank
fire(Tank t, Point target) {
// creates a bullet at the tanks' position, and adds it to the World
}
// within Bullet
update() {
// moves this bullet along its path to its target;
// if target reached, add an explosion there and destroy the bullet
}
// within Explosion
update() {
// advance to next explosion frame;
// or if finished, destroy the explosion object
}
You can read more on game event loops here and here.
Related
I'm trying to make a graphic project where a Ball runs away from my cursor, I already did the other way around where the ball seeks my cursor and when she arrives she loses velocity so it's like she's running fast until she comes around a range of 10 pixels and then she loses velocity until she touches the cursor.
The thing is, I can't find a way to make the ball run away from the cursor in a way that when I enter a diameter(from the ball), she runs slow, if I approach more she starts to run faster to get away but when my cursor leaves the diameter, she runs slow until she stops once again.
I hope I made it clear, I thought about a solution but I don't know if there's a library or some built function in Java that I could use guys:
-have like a percentage from 0 to 100 where the distance between my cursor and the ball fits inside, 0% is velocity=0, 100% is velocity=4 for example, do you have any idea if there is such thing that I could implement?
Thank you in advance!
I've made a Vector class where I change it and access the X and Y coordinates to make the ball move, I used basic trigonometry to make the vector ALWAYS the same length.
code of my ball (Chaser) class:
public class Chaser {
private double x;
private double y;
private double vel = 1;
private double hyp;
private Vector vector = new Vector(0, 0);
private double distance;
public Chaser(int width, int height){
x = width/2;
y = height/2;
}
public void setVel(Point m){
if(m.x != x)
hyp = Math.sqrt(Math.pow(Math.abs(m.x - x), 2) + Math.pow(Math.abs(m.y - y), 2));
else
hyp = Math.abs(m.y - y);
}
public void setDirection(Point m){
if(hyp == 0) return;
vector.change((m.x - x)/hyp, (m.y - y)/hyp);
}
public void draw(Graphics g){
g.setColor(Color.RED);
g.fillOval((int)x - 10, (int)y - 10, 20, 20);
g.setColor(Color.BLACK);
g.drawLine((int)x, (int)y, (int)(vector.getX()*15*vel) + (int)x, (int)(vector.getY()*15*vel) + (int)y);
}
public void move(Point m){
setVel(m);
setDirection(m);
useVector();
}
public void useVector(){
if(vector == null) return;
x -= vector.getX() * vel;
y -= vector.getY() * vel;
}
public void calculateVelocity(Point m){
if(vector == null) return;
// I don't know what to do yet
}
}
If you want to just push the ball around you can do something simple. Let's use vectors to make it easier to understand. Say ball holds the ball's center (x,y) and mouse contains the mouse position (x,y).
You can compute the distance between ball and mouse, that is (mouse - ball).length() to get how far away the mouse is from the ball.
If the distance > ball radius then the mouse is outside.
Otherwise you can do:
tmp = ball - mouse // get the vector from mouse to the ball.
tmp = tmp / tmp.length() * ball_radious // change the vector's length to match the radious of the ball.
ball = mouse + tmp // Move the ball such that the mouse will be on the edge.
As you move the mouse the ball will get pushed by the mouse.
If you want a bit of inertia, so the ball doesn't just stop when you don't push it anymore then you need to keep an additional vector speed and use tmp to get an acceleration.
Something like this:
tmp = ball - mouse // get the vector from mouse to the ball.
force = max(0, ball_radious - tmp.length()) // how strong we push the ball.
acceleration = tmp / tmp.legnth() * f(force) // compute the acceleration vector. f(force) is some function based on force, try k*f or k*f*f and see what looks better for your setup.
speed = speed * kDrag + acceleration // update the speed, kDrag should be between 0 and 1, start with something like 0.8 and try different values.
ball = ball + speed * time_delta // Update the ball's position.
You can play with the constants to get the right feel that you're looking for. time_delta is meant to normalize the speed between frams so you don't need to worry too much if there's some inconsistency between them. You can use a constant as well, but the movement might become jerky at times.
All operations above are vector operations.
So I was messing with Swing for the first time in a while and I came across a strange issue. So I am adding "shapes" to a list every so often, and then in the paintComponent method of a JPanel I am looping through the list and drawing the shapes. I also draw a shape outside of the for loop for testing purposes.
What happens is the shapes in the for loop will jump around the screen randomly. This only happens when the shapes are drawn in this loop.
What I have tried already:
Updating graphics drivers for both the integrated GPU and discrete GPU
Using java.util.Timer instead of Swing Timer
Using Thread/Runnable
Using things other than ArrayList, such as LinkedList, Vector, and a normal Array.
Trimmed literally everything out of my code except the basics, which is what we're left with here. I was drawing more complex things before when I noticed it.
Changed the timing (PERIOD variable, in millis). It will get worse if I increase or decrease it.
Changed from using System time in milliseconds to the System time in nanoseconds, converted to milliseconds. I know this should be the same but I was running out of ideas.
Here is a gif of the problem (15 seconds long):
image
You'll notice that the small squares will jump around at random intervals. This should not occur. I'm just trying to "spawn" a square at random coords.
Here is the code in a pastebin:
code
I have included all 3 classes in this order: the JPanel class, the Main class (extends JFrame), and the shape class
If any of the links don't work, inform me and I will promptly post other links.
Thanks.
This setup ...
#Override
public final void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (this.startTime == -1L) {
this.startTime = Main.timeMillis();
}
final long timeDiff = Main.timeMillis() - this.startTime;
if (this.circlesIndex <= 19 && timeDiff > 2000) {
final int randX = this.rand.nextInt(this.WIDTH);
final int randY = this.rand.nextInt(this.HEIGHT);
this.testShapes.add(new TestShape(randX, randY));
this.startTime = Main.timeMillis();
}
for (TestShape ts : this.testShapes) {
ts.draw(g);
}
g2.setColor(Color.gray);
g2.fill3DRect(350, 400, 100, 60, true);
}
#Override
public final void actionPerformed(final ActionEvent event) {
x++;
if (x > WIDTH) {
x = -50;
}
repaint();
}
is wrong.
Paint is for painting - you should not modify the state of the UI from inside any paint method, do this within your ActionListener. The problem is, your component can be painted for any number of reasons, many of which you don't control or know about
I'm working on learning about using events to change things on the GUI. I am currently using Controller Events passed from another method and I think I may be missing some key information about how variables work in these methods. Here is my source code (this is currently an inner class).
class MyDrawPanel extends JPanel implements ControllerEventListener{
static boolean isWriting = false;
public void controlChange(ShortMessage event) {
isWriting = true;
repaint();
}
public void paintComponent(Graphics g){
if (isWriting){
int red = (int) (Math.random() * 250);
int green = (int) (Math.random() * 250);
int blue = (int) (Math.random() * 250);
g.setColor(new Color(red, green, blue));
int xpos = (int) (Math.random() * 190 + 10);
int ypos = (int) (Math.random() * 190 + 10);
int width = (int) (Math.random() * 50 + 10);
int height = (int) (Math.random() * 50 + 10);
g.fillRect(xpos, ypos, width, height);
//isWriting = false;
}
What I'm trying to do is draw a new rectangle every time the listener receives an event but I can only get one of two things to happen. If I try to set "isWriting" back to false at the end of the if statement the if statement seems to continuously evaluate to false and not draw any rectangles. If I comment out that code to set isWriting back to false the drawing works for a single rectangle but there is nothing to change the variable back and call the repaint method so I only get a single rectangle.
I know that my events are getting to the listener as I've used a sout to increment a count every time it gets an event so it seems the problem is with the state of the isWriting variable. Thanks for the help!
You want to avoid changing the state of your class from within a painting method. This is not a method that you have full control over, and so doing this can lead to unexpected and difficult to debug side effects.
I'm not 100% certain of what you're trying to do, but if you're trying to draw a new rectangle each time a message is received, and you want that rectangle to persist (and in this way, perhaps show multiple rectangles), then,
Either draw the rectangles onto a BufferedImage, and then display the BufferedImage into your JPanel's paintComponent method, or
Create an ArrayList of an object that contains both a Rectangle and a Color, and then with each event, create a new one of these objects, add it to the list, and call repaint(). Then within paintComponent, iterate through the list drawing the rectangles.
Don't forget that you should always call the super's paintComponent method within your override.
Otherwise if you want the rectangle to display only for a period of time then use a Swing Timer. Or if when user acknowledges receipt of the message, then use an other listener. Again, please clarify your question.
Tutorials:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
I'm learning how to make an Applet using Java in the form of a game. In the game, I have a character sprite drawn at the center and moves when the player presses w a s d.
It goes like this:
public game extends applet implements KeyListener {
int x, y;
URL url;
Image image;
public void init() {
x = getSize().width/2;
y = getSize().height/2;
url = new URL(getCodeBase());
image = getImage(url, "player.gif"); //take note that this is a still image
addKeyListener(this);
}
public void paint(Graphics g) {
g.drawImage(image, x, y, 32, 32, this); //the size of the image is 32x32
}
public void KeyPressed(arg0) {
char c = arg0.getKeyChar();
switch(c) {
case 'w':
y -= 10;
break;
/*And so on. You guys know how it works.*/
}
repaint();
}
My problem is, the character sprite seems dull when the user doesn't press anything. What I want to do is to make the Image an array of images and put a simple image animation by looping the array in paint like so:
public void paint(Graphics g) {
for(int i = 0; ; i++) {
g.drawImage(image[i], x, y, 32, 32, this);
if(i == image.size() - 1) { i = 0;}
}
}
However, if I do this, I won't be able to get anymore KeyEvents that would activate when the user wants to move. My question is this: How will I make it so that my character does an animation when the program is "idle" (i.e. the user isn't pressing anything) while still maintaining the capability to take in KeyEvents (e.g. moving when the player types in w, a, s, or d, and then continuing the idle animation after repainting)?
Thanks in advance.
PS. I'm still quite a beginner in Java so sorry if my code is not very nice. Any advice is welcome.
You need to make your application multithreaded so that the painting runs in a separate thread.
Otherwise you will have the painting blocked while waiting for the next key.
This is not a trivial change to your code though.
Perhaps you would give yourself a new class, say InterestingCharacter, which can cycle through any of N states (corresponding to your N images). You clearly can't let any paint method run infinitely, but if your InterestingCharacter could render itself in its current state you might be onto something with that. Maybe it will be enough that this InterestingCharacter knows what state it is in and then some other object manages the rendering. Would it be helpful if the InterestingCharacter could tell you that its state has changed and so needs to be rendered again? If so, you could implement the Observer pattern such that the character is observed and your game an observer.
I think the trick will be to break the problem down into a few classes that have appropriate responsibilities--ideally a class should have one responsibility and each of its methods should do one thing.
Just some ideas to help you move forward. Experiment with it and see how it goes. Hope it helps!
I;m making a game in which I have to move little squares on an n by n grid, and they have to transition smoothly. This is the swap method I made which should be able to paint my transition on screen, but for some reason it is not doing it. I tried a much simpler version of my code on a simple project to move a Square back and forth and it worked like a charm, so I'm really not sure why this isn't repainting. This is just a bit of my code so if there's doubts about anything else on my code, ask away.
Thanks in advance. (:
public void swap( int y, int x ) {
long time = System.currentTimeMillis();
int counter = 0;
swapNum = tiles[y][x];
rect = (Rectangle) rectangles[y][x].clone();
while(counter < rect.height) {
if(System.currentTimeMillis() - time > 5) {
rect.translate(this.y-y, this.x-x);
time = System.currentTimeMillis();
counter++;
repaint();
}
}
swapNum = 0;
rect = new Rectangle();
int temporary = tiles[this.y][this.x];
tiles[this.y][this.x] = tiles[y][x];
tiles[y][x] = temporary;
this.x = x;
this.y = y;
}
If this block of code is running on the Event/Dispatch thread, which is used for drawing to the screen, then this will block the screen from updating.
Instead of doing the entire animation in one loop, consider designing an update method to do the animation, that will be called once every 15-30 milliseconds, and update the rectangle's position accordingly. Even better for smooth graphics is to draw to an image buffer and then have the actual draw method paint that buffer to the screen (double buffering).
Java3D has animations built-in, so it may be worth a look.