First post, so I'll try to be as clear as I can.
Basically, what I'm trying to do is create a little game, where you have a ship, and you can shoot bullets from it. The ship rotates accordingly to the player's deltaX and deltaY using a tangent formula. I use a standard game loop like this one:
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
#SuppressWarnings("unused")
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >=1){
tick();
delta--;
}
if(running)
repaint();
frames++;
if(System.currentTimeMillis() - timer > 1000)
{
timer += 1000;
frames = 0;
}
}
stop();
}
My "game" runs on a child of a JPanel, a class called Board. In the loop, I call the methods tick(), which updates the info, and repaint(), that works as a render method. This is my paintComponent(Graphics g) method:
public void paintComponent(Graphics g) {
//This is for background
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
g2d.fill(new Rectangle(0, 0, Constants.width, Constants.height));
//This is where actual game rendering occurs
handler.render(g);
g.dispose();
}
As can be seen, I dont render everything on my Board class. I do so on my handler. This is how I deal with the handler:
public void render(Graphics g) {
for (int i = 0; i < handlerList.size(); i++) {
//handlerList is a LinkedList
handlerList.get(i).render(g);
}
}
The LinkedList handlerList contains Entities.
Entities is an abstract class, which is parent of Creature, which is parent of Player and Bullet.
This is the code for the rendering of a Player instance:
public void render(Graphics g) {
float centerX = x + (width / 2);
float centerY = y + (height / 2);
double theta = findAngle(deltaX, deltaY);
Graphics2D g2d = (Graphics2D) g;
if(!stopped) g2d.rotate(theta, centerX, centerY);
else g2d.rotate(stoppedTheta, centerX, centerY);
g2d.drawImage(shipImage, (int)x, (int)y, (int)width, (int)height, null);
}
There is a boolean "stopped" which keeps track of the objects condition. I use Graphics2D instead of Graphics due to the fact that I wanna be able to rotate my ship.
Here's the code for the bullet's rendering:
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.yellow);
g2d.fillOval((int)x, (int)y, (int)width, (int)height);
}
It looks right, as far as I'm aware. Whenever I don't have a bullet on, the game runs fine, as you can see in this GIF:
DISCLAIMER: Sorry for low quality and the watermark, I've just formatted the computer and havent had time to install proper stuff....
When I add a bullet this happens:
The x and y position of the bullet doesn't change, but the bullet rotates with the ship. I'm assuming it has something to do with the misuse of the "dispose()" method, but I'm not sure what can be done to fix it.
Thank you in advance.
Don't ever call dispose on a Graphics context you did not explicitly create (or snapshot with create), this can cause issues further down the rendering pipeline.
Graphics is a shared context, so you need to be mindful of the changes you make to it and undo any "significant" changes you make, especially transformations
If it was me, I'd create a snapshot of the Graphics context before each call to render and the dispose of it after, for example
public void render(Graphics g) {
for (int i = 0; i < handlerList.size(); i++) {
//handlerList is a LinkedList
Graphics2D g2d = (Graphics2D)g.create();
handlerList.get(i).render(g2d);
g2d.dispose();
}
}
This ensures that what ever changes that render makes to the Graphics context are undone before the next element is rendered
If the changes are compounding, then I'd make the snapshot before the start of the loop and dispose of it after it.
In either case, it means you control the changes been made and how they affect other elements down the line.
Also, remember, transformations are compounding
Related
In Java, I can write pixel data to an image that is then printed to the screen by overridden methods (paint and paintComponent). I can refresh the screen easily by updating the image and calling refresh(), which calls the paint/paintComponent cycle.
I'm wanting to do this in swift (3) with a UIImage. I can update the image, but I can't figure out how to repeatedly project the image onto the screen, print the screen, and repeat as I need to refresh the screen.
Firstly, what type of swift project (Single view app., Game, etc.) is best for repeatedly refreshing the screen?
And secondly how do I go about creating a (regulated) loop in Swift that refreshes the screen every so often?
In java this would look like (in a class that extends Frame):
static int width = 1440;
static int height = 900;
private BufferedImage canvas;
static Color[][] RGBMap = new Color[width][height];
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.WHITE);
g2.clearRect(0, 0, width, height);
g2.drawImage(this.canvas, null, null);
paint();
}
public void paint() {
int i = 0;
while (i < width) {
int t = 0;
while (t < height) {
this.canvas.setRGB(i, t, RGBMap[i][t].getRGB());
t++;
}
i++;
}
//Refreshes RGBMap
iterate();
repaint();
}
I have very little experience with Java, and I am an amateur programmer. So mind my vocabulary.
I want to be able to stick a static rectangle on top of a rotating rectangle.
So far when I try to add another object it spins with the other image. I have tried setting the rotation to zero but that doesn't seem to work. I have also tried to create another class that draws components separately and added them to the frame using frame.add. I have also tried creating another part to the Draw class that has no effect on the GUI. Here is my current Draw class. Any help is appreciated.
class DrawRectangle extends JPanel {
#Override
public void paintComponent(Graphics g) {
int h = this.getHeight();
int w = this.getWidth();
Graphics2D g2 = (Graphics2D) g;
//draw background
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, w, h);
//draw roatiing rectangle
g2.setColor(Color.CYAN);
Rectangle rRec = new Rectangle(w / 4, h / 4, 2 * w / 4, 2 * h / 4);
double wr = rRec.getX() + rRec.getWidth() / 2;
double hr = rRec.getY() + rRec.getHeight() / 2;
g2.rotate(Math.toRadians(count), wr, hr);
g2.fill(rRec);
g2.fillRect(w / 3, h / 3, 2 * w / 3, 2 * h / 3);
}
public void paintComponent2(Graphics g) {
int h = this.getHeight();
int w = this.getWidth();
Graphics2D g2 = (Graphics2D) g;
}
}
So far when I try to add another object it spins with the other image.
Create a separate Graphics object to do the rotation so you don't affect the properties of the Graphics object passed into the painting method:
//Graphics2D g2 = (Graphics2D) g;
Graphics2D g2 = (Graphics2D)g.create();
// painting code
g2.dispose();
Move your g2.fill(rRec); BEFORE the rotate call, and it should work (I just tested it out).
This way, you will draw your static rectangle before the rotation, perform the rotation, THEN draw your second rectangle. Assuming your count variable is incremented somewhere, it should show the second rectangle being rotated.
I have a simple program that draws the trajectory of a particle launched from the origin at a certain speed and angle. I created a subclass of JPanel to handle the drawing of this. My everytime my subclass is redrawn it takes the difference between the current time and the initial time(both in milliseconds), converts this to seconds, then finds the x and y coordinate of where the particle should be at that point in time, and finally takes those x and y coordinates and draws them on the screen. My problem is that my subclass seems to be redrawn at interval that seem long because there are only a few dots that are shown.
My drawing method:
private void doDrawing(Graphics g) {
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
Graphics2D g2d = (Graphics2D) g;
g.drawString("Acceleration: -9.8m/s i", 0, 20);
StringBuilder b = new StringBuilder();
b.append("Current Velocity: ");
b.append(String.valueOf(sim.getVector(tickSpeed
* ((System.currentTimeMillis() - initTime) / 1000)).getMagnitude()));
b.append(" m/s at ");
b.append(String.valueOf(sim.getVector(tickSpeed
* ((System.currentTimeMillis() - initTime) / 1000)).getDirection().getDirectionDeg()));
b.append(" degrees");
g.drawString(b.toString(), 0, 30);
drawPreviousPoints(g2d);
drawCurrentPointAndAppend(g2d, w, h);
repaint();
}
private void drawCurrentPointAndAppend(Graphics2D g2d, int w, int h) {
g2d.setColor(Color.red);
double height = (length / w) * h;
Vector2D c = sim.getVector(tickSpeed
* ((System.currentTimeMillis() - initTime) / 1000));
double currentX = w
* ((sim.getX(tickSpeed
* ((System.currentTimeMillis() - initTime) / 1000))) / length);
double currentY = h
* (1 - ((sim.getY(tickSpeed
* ((System.currentTimeMillis() - initTime) / 1000))) / height));
g2d.drawLine((int) currentX, (int) currentY, (int) currentX,
(int) currentY);
g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER));
g2d.drawLine((int) currentX, (int) (currentY),
(int) (currentX + w * (c.getX() / length)),
(int) (currentY + (h * -(c.getY() / height))));
xList.add(currentX);
yList.add(currentY);
}
private void drawPreviousPoints(Graphics2D g2d) {
g2d.setColor(Color.blue);
g2d.setStroke(new BasicStroke(7, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
if (!xList.isEmpty()) {
for (int i = 0; i < xList.size(); i++) {
g2d.drawLine(xList.get(i).intValue(), yList.get(i).intValue(),
xList.get(i).intValue(), yList.get(i).intValue());
}
}
}
tickSpeed is just a variable that I use to speed up or slow down the particle. It runs fine; however, the animation seems very choppy.
How do I fix this choppiness(make everything seem more "fluid")
Where should I call repaint()? Because I feel like calling it at the end of my drawing method isn't right.
An important rule of Swing- You don't control the paint process...
Don't perform these calculations within the paintComponent. The paintComponent is meant to paint the current state of the UI and may be called at any time for many reasons, most of which are outside your control.
Instead, consider using a javax.swing.Timer set to repeat at a regular interval (40ms is 25 ticks a second).
Set up a model which keeps track of the particles current been processed. When the timer ticks, calculate your particle positions and update them, then call repaint.
Within your paintComponent, simply paint the current state of your model.
Have a look at Concurrency in Swing and How to use Swing Timers for more details
The paint process is internally handled so you can not control the frequency of it's execution.
However, you can create separate threads or timers which can invoke processes at your desired frequency. Use the paint method only to render on your canvas, do other logic and processing in another function.
For a game that I'm working on I need to draw a rectangle that gets smaller and smaller. I have figured out how to draw the rectangle smaller by using a swing Timer like this:
timer = new Timer(100, new ActionListener(){
public void actionPerformed(ActionEvent e){
Graphics2D g2d = (Graphics2D) panel.getGraphics();
if(width > 64){
g2d.drawRect(x,y,width,height);
x += 1;
y += 1;
width -= 1;
height -= 1;
}
}
});
timer.start();
The problem I am having is that it wont remove the rectangle that was drawn before so it wont look like it's shrinking but more like it's filling in. So how would I remove the previously drawn rectangle right after the smaller rectangle have been drawn?
You might start with:-
Change:
Graphics2D g2d = (Graphics2D) panel.getGraphics();
to:
repaint();
The Graphics instance from getGraphics() is transient, the window might be repainted whenever the JVM feels it is necessary.
The overridden method might look like this.
#Override
public void paintComponent(Graphics g){
super.paintComponent(g); // Effectively clears the BG
Graphics2D g2d = (Graphics2D)g;
if(width > 64){
g2d.drawRect(x,y,width,height);
x += 1;
y += 1;
width -= 1;
height -= 1;
}
// Toolkit.getDefaultToolkit().sync();
// g2d.dispose(); NO! Don't dispose of this graphics instance
}
I wrote a code in Java (using swing) which draws few polygons on a panel.
public MyClass extends JPanel
The code is very simple (but long) and basically adds few Polygons, then adds few points to each polygon and then draw them on the screen (with drawPolygon).
My problem is when I run the program, I can't see the drawings on the panel.
After a while, I figure out that when I re-size my frame, I can suddenly see the drawing but it duplicates itself many times (depends how much I re-size). If I play enough time with the resizing, I get:
java.lang.OutOfMemoryError: Java heap space
Also, myPolygon.invalidate() doesn't help.
When using setResizable(false) I can't see my drawing at all.
Does anyone have a solution?
Duplicate Image Screenshot:1
To start with, in your paintComponent method, don't call
setPreferredSize(new Dimension(500,500));
setVisible(true);
validate();
This will request a repaint, cause paintComponent to be recalled and you'll end up in a nasty loop, consuming your CPU and (as you have found out), your memory.
IF you can get away with it, you're better off to draw the polygon to a buffer and draw the buffer to the screen on each iteration of the paintComponent. This will be faster in the long run...
// Create a field
private BufferedImage buffer;
// Call this when you need to change the polygon some how...
protected void createBuffer() {
// You need to determine the width and height values ;)
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
int xoffset=5;//Multiply in order to "zoom" the picture
int offset=0;//moves shape to the right
p.addPoint(40*xoffset-offset, 30*xoffset-offset);
p.addPoint(50*xoffset-offset,30*xoffset-offset);
p.addPoint(57*xoffset-offset,37*xoffset-offset);
p.addPoint(57*xoffset-offset,47*xoffset-offset);
p.addPoint(50*xoffset-offset,54*xoffset-offset);
p.addPoint(40*xoffset-offset,54*xoffset-offset);
p.addPoint(33*xoffset-offset,47*xoffset-offset);
p.addPoint(33*xoffset-offset, 37*xoffset-offset);
g.drawPolygon(p);
g.dispose();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buffer != null) {
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(buffer, translateX, translateY, this);
}
}
UPDATE
So, anyway, the other fun things you're doing are...
Creating a static reference to your Polygon. Hope you weren't intending to have more the one on the screen at a time ;)
Add new points to an already existing polygon (each time paintComponent was called)
Translating the polygon each time paintComponent was called
Try something like this instead
public class RoundTop extends JPanel {
//Polygons declarations
private Polygon p = new Polygon();
//Translate variables;
private int translateX = 10;
private int translateY = 10;
public RoundTop() {
int xoffset = 5;//Multiply in order to "zoom" the picture
int offset = 0;//moves shape to the right
p.addPoint(40 * xoffset - offset, 30 * xoffset - offset);
p.addPoint(50 * xoffset - offset, 30 * xoffset - offset);
p.addPoint(57 * xoffset - offset, 37 * xoffset - offset);
p.addPoint(57 * xoffset - offset, 47 * xoffset - offset);
p.addPoint(50 * xoffset - offset, 54 * xoffset - offset);
p.addPoint(40 * xoffset - offset, 54 * xoffset - offset);
p.addPoint(33 * xoffset - offset, 47 * xoffset - offset);
p.addPoint(33 * xoffset - offset, 37 * xoffset - offset);
p.translate(translateX, translateY);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawPolygon(p);
g2d.dispose();
}
}