Why does the clock flicker? - java

The following code is an animation of a clock.
What changes would have to be made in the program to make it not flicker?
Why does the field initiation have to take place in the addNotify() function for the hands to be displayed?
You can copy all of it into one .java file and it should work (flicker ;) ).
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class ac extends Frame implements Runnable {
// for double buffering
Graphics og;
Image oi;
Thread t = null;
int width, height;
int timeH, timeM, timeS, radius = 50, lenH, lenM, lenS,
lenIn, cx, cy, x1, y1, x2, y2;
private boolean timeToQuit;
ac() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
stopRunning();
dispose();
}
});
setMinimumSize(new Dimension(300, 300));
EventQueue.invokeLater(new Runnable() {
public void run() {
setVisible(true);
}
});
}
public void addNotify() {
super.addNotify();
width = getBounds().width;
height = getBounds().height;
setSize(width, height);
lenH = 5 * radius / 10;
lenM = 6 * radius / 10;
lenS = 7 * radius / 10;
lenIn = 8 * radius / 10;
cx = width / 2;
cy = height / 2;
// for double buffering
oi = createImage(width, height);
og = oi.getGraphics();
}
public static void main(String [] args) {
ac clock = new ac();
clock.start();
}
public void start() {
if (t == null) {
t = new Thread(this);
t.start();
}
}
public void stopRunning() {
timeToQuit = true;
}
public void run() {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
do {
repaint();
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
}
}
while (!timeToQuit);
System.out.println("Quitting");
}
public void paint(Graphics g) {
og.setColor(new Color(170, 170, 170));
og.fillRect(0, 0, width, height);
Calendar cal = new GregorianCalendar();
timeH = cal.get(Calendar.HOUR);
timeM = cal.get(Calendar.MINUTE);
timeS = cal.get(Calendar.SECOND);
if (timeH >= 12)
timeH -= 12;
for (int i = 1 ; i < 13 ; i++) {
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + radius * Math.sin(i * 2 * 3.14159f / 12));
y2 = (int) (cy - radius * Math.cos(i * 2 * 3.14159f / 12));
if (i % 3 != 0) {
x1 = (int) (cx + 0.9f * radius * Math.sin(i * 2 * 3.14159f / 12));
y1 = (int) (cy - 0.9f * radius * Math.cos(i * 2 * 3.14159f / 12));
}
else {
x1 = (int) (cx + 0.8f * radius * Math.sin(i * 2 * 3.14159f / 12));
y1 = (int) (cy - 0.8f * radius * Math.cos(i * 2 * 3.14159f / 12));
}
og.drawLine(x1, y1, x2, y2);
og.setColor(new Color(230, 230, 230));
og.drawLine(x1 - 1, y1 - 1, x2 - 1, y2 - 1);
}
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenH * Math.sin((timeH + timeM / 60.0f + timeS / 3600.0f)
* 2 * 3.14159f / 12));
y2 = (int) (cy - lenH * Math.cos((timeH + timeM / 60.0f + timeS / 3600.0f)
* 2 * 3.14159f / 12));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.red);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenM * Math.sin((timeM + timeS / 60.0f) * 2 * 3.14159f
/ 60));
y2 = (int) (cy - lenM * Math.cos((timeM + timeS / 60.0f) * 2 * 3.14159f
/ 60));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.green);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenS * Math.sin(timeS * 2 * 3.14159f / 60));
y2 = (int) (cy - lenS * Math.cos(timeS * 2 * 3.14159f / 60));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.blue);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
og.drawOval((width - 2 * radius) / 2, (height - 2 * radius) / 2,
2 * radius, 2 * radius);
og.setColor(new Color(230, 230, 230));
og.drawOval((width - 2 * radius) / 2 - 1, (height - 2 * radius) / 2 - 1,
2 * radius, 2 * radius);
g.drawImage(oi, 0, 0, this);
}
}

This uses awt which is old and it paints on to the main Frame - not the best way to make a swing UI. You can try this :
make the timer - sleep amount to less or more, try 300 or 800
use a Panel to draw on, add the panel to the frame
use swing (use a JComponent, it double buffers automatically, and a JFrame
For 2 and 3 you can keep original class to make Frame/JFrame and a new class for the java.awt.Panel or javax.swing.JComponent which is added to the former.
Does not flicker on my mac system, so i think its an OS + times its refreshed issue. Meaning suggestion 1 might work - try that first. Talking about line of code :
Thread.sleep(800);

Believe it or not, the problem was solved by adding the function:
public void update(Graphics g)
{
paint(g);
}
But the question remains, why? And why aren't the hands displayed when you initiate the class fields outside the addNotify() function?

Related

How to fix the clock from flickering?

I am currently making an analog clock in java using AWT Package and Swing. But the digital clock overlay is currently flickering every once in a while and I'd like to fix that.
I've read about implementing double buffered in conjunction with repaint() but I am stuck on how to implement it.
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
public class Clock2d extends Applet {
GregorianCalendar cal;
Timer clockTimer = new Timer();
TimeZone clockTimeZone = TimeZone.getDefault();
public Clock2d() {
clockTimer.schedule(new TickTimerTask(), 0, 1000);
}
#Override
public void init() {
}
public void paint(Graphics g) {
g.setColor(Color.BLUE);
g.fillOval(40, 40, 220, 220);
g.setColor(Color.WHITE);
g.fillOval(50, 50, 200, 200);
double second = cal.get(Calendar.SECOND);
double minute = cal.get(Calendar.MINUTE);
double hours = cal.get(Calendar.HOUR);
for (int i = 0; i < 60; i++) {
int length = 90;
double rad = (i * 6) * (Math.PI) / 180;
if (i % 5 == 0) {
length = 82;
g.setColor(Color.BLUE);
} else {
g.setColor(Color.GRAY);
}
int x = 150 + (int) (95 * Math.cos(rad - (Math.PI / 2)));
int y = 150 + (int) (95 * Math.sin(rad - (Math.PI / 2)));
int x1 = 150 + (int) (length * Math.cos(rad - (Math.PI / 2)));
int y1 = 150 + (int) (length * Math.sin(rad - (Math.PI / 2)));
g.drawLine(x, y, x1, y1);
}
drawHands(g, second, minute, hours);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
g.setColor(Color.BLUE);
g.setFont(new Font("Tahoma", Font.BOLD, 16));
g.drawString(sdf.format(cal.getTime()), 120, 20);
g.setFont(new Font("Arial", Font.BOLD, 10));
}
public void drawHands(Graphics g, double second, double minute, double hours) {
double rSecond = (second * 6) * (Math.PI) / 180;
double rMinute = ((minute + (second / 60)) * 6) * (Math.PI) / 180;
double rHours = ((hours + (minute / 60)) * 30) * (Math.PI) / 180;
g.setColor(Color.RED);
g.drawLine(150, 150, 150 + (int) (100 * Math.cos(rSecond - (Math.PI / 2))), 150 + (int) (100 * Math.sin(rSecond - (Math.PI / 2))));
g.setColor(Color.BLACK);
g.drawLine(150, 150, 150 + (int) (70 * Math.cos(rMinute - (Math.PI / 2))), 150 + (int) (70 * Math.sin((rMinute - (Math.PI / 2)))));
g.drawLine(150, 150, 150 + (int) (50 * Math.cos(rHours - (Math.PI / 2))), 150 + (int) (50 * Math.sin(rHours - (Math.PI / 2))));
}
class TickTimerTask extends TimerTask {
#Override
public void run() {
cal = (GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
repaint();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Clock 2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(330, 330));
Clock2d clock2d = new Clock2d();
clock2d.setPreferredSize(new Dimension(320, 320));
clock2d.init();
frame.setLayout(new BorderLayout());
frame.getContentPane().add(clock2d, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
The code work exactly as intended aside from the occasional flickering of the Numbered Digital clock.
public class Clock2d extends Applet A java.awt.Applet is not only deprecated, but also not double buffered by default. Change that to extend a JPanel (which is).
Then change public void paint(Graphics g) { to public void paintComponent(Graphics g) { super.paintComponent(g); to respect the paint chain.
class TickTimerTask extends TimerTask use a javax.swing.Timer instead. It runs on the Event Dispatch Thread, and any calls to the GUI should be made on the EDT.
Here are those three points, expressed in the code. Do you see the 'flickering' artifacts in this version?
import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import javax.swing.*;
public class Clock2d extends JPanel {
TimeZone clockTimeZone = TimeZone.getDefault();
GregorianCalendar cal =
(GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
ActionListener repaintListener = (ActionEvent e) -> {
cal = (GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
repaint();
};
Timer clockTimer = new Timer(100, repaintListener);
public Clock2d() {
clockTimer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(40, 40, 220, 220);
g.setColor(Color.WHITE);
g.fillOval(50, 50, 200, 200);
double second = cal.get(Calendar.SECOND);
double minute = cal.get(Calendar.MINUTE);
double hours = cal.get(Calendar.HOUR);
for (int i = 0; i < 60; i++) {
int length = 90;
double rad = (i * 6) * (Math.PI) / 180;
if (i % 5 == 0) {
length = 82;
g.setColor(Color.BLUE);
} else {
g.setColor(Color.GRAY);
}
int x = 150 + (int) (95 * Math.cos(rad - (Math.PI / 2)));
int y = 150 + (int) (95 * Math.sin(rad - (Math.PI / 2)));
int x1 = 150 + (int) (length * Math.cos(rad - (Math.PI / 2)));
int y1 = 150 + (int) (length * Math.sin(rad - (Math.PI / 2)));
g.drawLine(x, y, x1, y1);
}
drawHands(g, second, minute, hours);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
g.setColor(Color.BLUE);
g.setFont(new Font("Tahoma", Font.BOLD, 16));
g.drawString(sdf.format(cal.getTime()), 120, 20);
g.setFont(new Font("Arial", Font.BOLD, 10));
}
public void drawHands(Graphics g, double second, double minute, double hours) {
double rSecond = (second * 6) * (Math.PI) / 180;
double rMinute = ((minute + (second / 60)) * 6) * (Math.PI) / 180;
double rHours = ((hours + (minute / 60)) * 30) * (Math.PI) / 180;
g.setColor(Color.RED);
g.drawLine(150, 150, 150 + (int) (100 * Math.cos(rSecond - (Math.PI / 2))), 150 + (int) (100 * Math.sin(rSecond - (Math.PI / 2))));
g.setColor(Color.BLACK);
g.drawLine(150, 150, 150 + (int) (70 * Math.cos(rMinute - (Math.PI / 2))), 150 + (int) (70 * Math.sin((rMinute - (Math.PI / 2)))));
g.drawLine(150, 150, 150 + (int) (50 * Math.cos(rHours - (Math.PI / 2))), 150 + (int) (50 * Math.sin(rHours - (Math.PI / 2))));
}
public static void main(String[] args) {
// Swing / AWT GUIs should be created & changed on the EDT ..
Runnable r = () -> {
JFrame frame = new JFrame("Clock 2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(330, 330));
Clock2d clock2d = new Clock2d();
clock2d.setPreferredSize(new Dimension(320, 320));
frame.setLayout(new BorderLayout());
frame.getContentPane().add(clock2d, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
};
// .. this is how we ensure that Runnable is on the EDT.
SwingUtilities.invokeLater(r);
}
}
Further tip re fonts:
g.setFont(new Font("Arial", Font.BOLD, 10));
Given the paint method is called repeatedly, it would be best to establish the fonts (both of them) when the class is constructed and store them a attributes of the class (which can be referenced in the methods).
But better to use logical fonts instead of something like 'Arial', like this:
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 10));
Not only does that provide compile time checking, but it adapts across OS platforms. For example, the SANS_SERIF font would result in Arial on Windows and probably most *nix boxes, but on OS X the default SANS_SERIF (undecorated) font is Helvetica.
The other font, Tahoma, is more problematic. Again it's likely to be present on many Windows boxes, but it might be best to either test for it and have a list of backups, or supply the font with the clock app. (assuming you have distribution rights).

LibGDX Actor in stage not receiving click events

Edit:
I think it's something to do with the way I setBounds()...
A similar project I have (that actually works), involves a moving box that changes color when clicked... A box - not a circle. I think this is a clue.
OG Post:
Subclassed Actor.
Put Actor in a Stage.
Added a ClickListener inside Actor.constructor.
Set Stage as inputProcessor.
But when I click on the Actor, nothing seems to happen.
There is no response - when I try to click the actor.
It's suppose to change color, increase speed and track input x/ys.
It's weird 'cause I built a very similar program that works. So why doesn't this?
The actor is represented by a circle drawn by the ShapeRenderer. I assume it's "click" boundary must be just a rectangle?
HelloGDXGame.java
#Override
public void create() {
SCREEN_WIDTH = Gdx.graphics.getWidth() / 2;
SCREEN_HEIGHT = Gdx.graphics.getHeight() / 2;
rowHeight = 80;
touchPos = new Vector3();
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.PINK);
font.setScale(4.0f);
ball = new Ball(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 180, 90, this);
stage = new Stage(new StretchViewport(SCREEN_WIDTH, SCREEN_HEIGHT));
stage.addActor(ball);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render()
{
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Translate inputs x/y
touchPos.x = Gdx.input.getX();
touchPos.y = (Gdx.input.getY() - Gdx.graphics.getHeight()) * -1;
stage.act();
stage.draw();
// UI, to check it all works (it doesn't)
batch.begin();
font.draw(batch, "Ball Class: Taps: " + ball.getTaps(), 64, SCREEN_HEIGHT - (rowHeight * 8));
font.draw(batch, "Main Class: Touch X: " + touchPos.x, 64, SCREEN_HEIGHT - (rowHeight * 7));
font.draw(batch, "Main Class: Touch Y: " + touchPos.y, 64, SCREEN_HEIGHT - (rowHeight * 6));
font.draw(batch, "Ball Class: Touch X: " + ball.getScreenX(), 64, SCREEN_HEIGHT - (rowHeight * 5));
font.draw(batch, "Ball Class: Touch Y: " + ball.getScreenY(), 64, SCREEN_HEIGHT - (rowHeight * 4));
batch.end();
}
Ball.java
public Ball(float xPos, float yPos, float xSpeed, float ySpeed, HelloGdxGame game) {
super();
this.game = game;
renderer = new ShapeRenderer();
taps = 0;
radius = 196;
super.setBounds(xPos - radius, yPos - radius, radius * 2, radius * 2);
// just to check it's working
screenXY = new Vector3(0, 0, 0);
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.setTouchable(Touchable.enabled);
this.addListener(new ClickListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
doStuff(x, y);
return false;
}
});
changeColour(); // sets random color
}
private void doStuff(float screenX, float screenY) {
screenXY.x = screenX;
screenXY.y = screenY;
changeColour();
taps++;
// Increase speed
if (xSpeed > 0) xSpeed++;
else xSpeed--;
if (ySpeed > 0) ySpeed++;
else ySpeed--;
}
#Override
public void act(float delta) {
super.act(delta);
// move
super.setX(super.getX() + (xSpeed * delta));
super.setY(super.getY() + (ySpeed * delta));
// Bounce horizontally
if (super.getX() < 0 || super.getX() + (radius / 2) > Gdx.graphics.getWidth()) {
super.setX(super.getX() - (xSpeed * delta));
xSpeed *= -1;
}
// Bounce vertically
if (super.getY() < 0 || super.getY() + (radius / 2) > Gdx.graphics.getHeight()) {
super.setY(super.getY() - (ySpeed * delta));
ySpeed *= -1;
}
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setColor(r, g, b, 1);
renderer.circle(super.getX(), super.getY(), radius);
renderer.end();
}
1) Take a changed Ball from here
2)
Change
SCREEN_WIDTH = Gdx.graphics.getWidth() / 2;
SCREEN_HEIGHT = Gdx.graphics.getHeight() / 2;
To
SCREEN_WIDTH = Gdx.graphics.getWidth();
SCREEN_HEIGHT = Gdx.graphics.getHeight();
This is illogical.

Drawing a recursive snow flake

I have this problem where I have to draw a recursive snow flake such that the flake has 6 lines coming from a center point, each 60 degrees apart from one another, then the endpoint of each of those lines, is the center point of another flake. Each time it branches off to another flake, the length is cut in half. The length is defined as just one side of the flake. This recursion continues to happen until the length is down to a distance such as 20 pixels where you can no longer distinguish the different flakes.
I understand that recursion is just a method that calls itself, but I'm just having difficulty setting up this problem. What I've got so far is the math to draw the initial flake but I just don't know how to set up the recursion to draw the flakes.
Here's what I have so far.
import java.awt.*;
import java.util.Random;
import javax.swing.*;
public class SnowFlake extends JPanel {
private MainWindow panel;
private int x1OfFlake = 400;
private int y1OfFlake = 400;
private int maxLength = 200; // this is length of, 1 0f 6 branches for each individual flake
public SnowFlake() {
// generateRandCoodinatesLength();
}
public void generateRandCoodinatesLength() {
Random rn = new Random();
maxLength = rn.nextInt(200) + 100;
x1OfFlake = rn.nextInt(700) + 100;
y1OfFlake = rn.nextInt(700) + 100;
}
public void paint(Graphics g) {
drawFlake(levels, x1OfFlake, y1OfFlake, g); // x1, y1, x2, y2
}
public void drawFlake(int level, int x1, int y1, Graphics g){
//below was just how I made sure my picture was correct
/*
g.drawLine(x1, y1, 600, 400); //1
g.drawLine(x1, y1, 500, 227); //2
g.drawLine(x1, y1, 300, 227); //3
g.drawLine(x1, y1, 200, 400); //4
g.drawLine(x1, y1, 300, 573); //5
g.drawLine(x1, y1, 500, 573); //6
*/
g.drawLine(x1, y1, x1 + maxLength, y1); // 1
g.drawLine(x1, y1, x1 + (maxLength / 2), y1 - ((int) ((maxLength / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (maxLength / 2), y1 - ((int) ((maxLength / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - maxLength, y1); // 4
g.drawLine(x1, y1, x1 - (maxLength / 2), y1 + ((int) ((maxLength / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (maxLength / 2), y1 + ((int) ((maxLength / 2) * Math.sqrt(3)))); // 6
}
}
This is what the end result should kind of look like, except with more branches drawn.
picture
This could bring you closer to what you want
public void drawFlake(int level, float angleDegrees, Graphics g) {
/*
* Exit condition
* If the max number of levels has been reached,
* or the maxLength is no longer visible when drawn
*/
if (level >= MAX_LEVEL || maxLength == 0) {
return;
}
/*
* Secondary condition, increment the level if we've gone around the
* circle once
*/
if (angleDegrees >= 360) {
maxLength *= .9;
drawFlake(level + 1, 0, g);
return;
}
g.drawLine(
centerX,
centerY,
centerX + (int) (maxLength * Math.sin(Math.toRadians(angleDegrees))),
centerY + (int) (maxLength * Math.cos(Math.toRadians(angleDegrees))));
int currentLevelAngleIncrement = 60 / (level + 1);
drawFlake(level, angleDegrees + currentLevelAngleIncrement, g);
}
Ends up something like this..
Not sure what level is - doesn't appear to be used in the drawFlake method...
I would change your recursive function to take the x and y coordinates and the length of the current line:
public void drawFlake(int x1, int y1, int length, Graphics g) {
// draw the current flake - exactly the same as your method except that it
// uses the length passed not the maximum length
g.drawLine(x1, y1, x1 + length, y1); // 1
g.drawLine(x1, y1, x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - length, y1); // 4
g.drawLine(x1, y1, x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 6
// recursion
int newLength = length / 2;
if (newLength >= MIN_LENGTH) {
// this is the recursive bit - call the function again for each of the end points
drawFlake(x1 + length, y1, newLength, g);
drawFlake(x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - length, y1, newLength, g);
drawFlake(x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
}
}
Then just kick it off with the start values:
public void paint(Graphics g) {
drawFlake(x1OfFlake, y1OfFlake, maxLength, g);
}
Note that the coordinates passed to the recursive drawFlake methods are the same as the end points of the drawLine calls, so you could calculate these once and reuse them.
Also, for readability (and maybe performance), I'd just calculate (length / 2) * Math.sqrt(3) once at the start of each call.
Haven't run it, so may be some typos, but should get you on the right lines.
Thanks for your help, the code below helped a lot with the recursion. It makes more sense now that I have to pass those variables into the method so they are actually changing. Also with this, I noticed that if you only divide the length by two it just forms a hexagonal shape, filled with triangles. So messing around with that and the minimum length solved that problem.
public void drawFlake(int x1, int y1, int length, Graphics g){
g.drawLine(x1, y1, x1 + length, y1); // 1
g.drawLine(x1, y1, x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - length, y1); // 4
g.drawLine(x1, y1, x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 6
//recursion
int newLength = length/3; //changed this to 3 to make it more look more like a flake
if(newLength >= minLength){
drawFlake(x1 + length, y1, newLength, g);
drawFlake(x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - length, y1, newLength, g);
drawFlake(x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
}

How to create mutiple mobs entities with the current given code?

I am currently making a tower shooter type game that has a turret at the bottom of the screen. It shoots multiple bullets (or is suppose too, I haven't made it work properly yet) ever few second or so. I have a enemy guy come from the top of the screen and make his way to the bottom unless the bullets collide with the enemy. If it does collide, the y value is set back to to the top of the screen and the enemy comes down again from another x value position. What I am trying to do is make multiple enemies come down from the top, one after the other in different x value positions, but I can't figure out how to make multiple version of the enemy with the collision checking it too.
private Graphics2D graphics;
private Animation mobAnimation = new Animation();
private int mobX = (int) ((Math.random() * 1252) + 28);
private int mobY = -55;
private int bulletX = ((width / 2) - (22 / 2));
private int bulletY = (height - 100);
private Timer mobs = new Timer(200, new MobListener());
private Timer bullet = new Timer(100, new BulletListener());
private BufferedImage MobAnimation;
// Draws the bullets.
public void drawBullets() {
// bulletCollision = new Ellipse2D.Double(bulletX, bulletY, 22, 22);
BufferedImage Bullets = bullets.getSprite(0, 0, 13, 13);
float centerX = (getWidth() / 2);
float centerY = (getHeight() - (75 / 2));
double angle = Math.atan2((centerY - mouse.getMouseY()), (centerX - mouse.getMouseX())) - (Math.PI / 2);
graphics.rotate(angle, (getWidth() / 2), (getHeight() - (75 / 2)));
graphics.drawImage(Bullets, bulletX, bulletY, 22, 22, null);
// graphics.setColor(Color.WHITE);
// graphics.draw(bulletCollision);
graphics.rotate(-angle, (getWidth() / 2), (getHeight() - (75 / 2)));
}
// Part of the bullets.
private class BulletListener implements ActionListener {
public void actionPerformed(ActionEvent bae) {
bulletY -= 20;
if (bulletY <= -350) {
bulletY = (getHeight() - 100);
}
if (bulletCollisionCheck()) {
mobY = -55;
bulletY = getHeight() - 100;
mobX = (int) ((Math.random() * 1252) + 28);
finalScore += 10;
}
}
}
// Draws the mobs.
public void drawMobs() {
// mobCollision = new Ellipse2D.Double(((mobX - (56 / 2)) - 3), (mobY - 56 / 2), 56, 56);
BufferedImage Mob1[] = new BufferedImage[4];
Mob1[0] = mob1.getSprite(0, 0, 32, 35);
Mob1[1] = mob1.getSprite(32, 0, 32, 35);
Mob1[2] = mob1.getSprite(64, 0, 32, 35);
Mob1[3] = mob1.getSprite(32, 0, 32, 35);
for (int i = 0; i < Mob1.length; i++) {
mobAnimation.addFrame(Mob1[i]);
}
graphics.drawImage(MobAnimation, (mobX - (56 / 2)), (mobY - (56 / 2)), 52, 55, null);
// graphics.setColor(Color.WHITE);
// graphics.draw(mobCollision);
}
// Part of the mobs.
private class MobListener implements ActionListener {
public void actionPerformed(ActionEvent mae) {
MobAnimation = mobAnimation.getNextFrame();
mobY += 10;
float dx = (mobX - (getWidth() / 2));
float dy = (mobY - (getHeight() - 2));
float radiusSum = ((56 / 2) + (256 / 2));
float distanceBetweenCircles = (float) Math.sqrt((dx * dx) + (dy * dy));
float distanceToMove = radiusSum - distanceBetweenCircles;
if (mobY >= (getHeight() + 55)) {
mobY = -55;
health -= 10;
}
if (health <= 15) {
gameState = GAME_OVER;
health = 300;
mobs.stop();
scenes.stop();
timer.stop();
mobY -= 55;
}
if (mobCollisionCheck()) {
if (mobX < (getWidth() / 2)) {
mobX -= distanceToMove;
} else if (mobX > (getWidth() / 2)) {
mobX += distanceToMove;
} else if (mobX == (getWidth() / 2)) {
mobX += distanceToMove;
}
}
}
}
This is where it checks for collision:
// Checks the collision of mobs.
public boolean mobCollisionCheck() {
float dx = (mobX - (getWidth() / 2));
float dy = (mobY - (getHeight() - 2));
float radiusSum = ((56 / 2) + (256 / 2));
float distance = (dx * dx) + (dy * dy);
return distance <= radiusSum * radiusSum;
}
// Checks the collision of bullets.
public boolean bulletCollisionCheck() {
float centerX = (getWidth() / 2);
float centerY = (getHeight() - (75 / 2));
double angle = Math.atan2((centerY - mouse.getMouseY()), (centerX - mouse.getMouseX())) - (Math.PI / 2);
float dxB = (bulletX - (getWidth() / 2));
float dyB = (bulletY - (getHeight() - (75 / 2)));
float distanceBetweenCircles = (float) Math.sqrt((dxB * dxB) + (dyB * dyB));
float bulletXB = (float) ((getWidth() / 2) + (distanceBetweenCircles * Math.sin(angle)));
float bulletYB = (float) ((getHeight() - (75 / 2)) - (distanceBetweenCircles * Math.cos(angle)));
float dx = (mobX - (bulletXB + 3));
float dy = (mobY - bulletYB);
float radiusSum = ((56 / 2) + (22 / 2));
float distance = (dx * dx) + (dy * dy);
return distance <= radiusSum * radiusSum;
}
If someone can please help. I have tried creating the mobs in a different class but then I have to change the x value to a random for each of the different enemies which causes a lot of errors that I can't seem to fix.
EDIT:
Forgot to add the animation class.
public class Animation {
private ArrayList<BufferedImage> Frames = new ArrayList<BufferedImage>();
private int currentFrame = 0;
public void addFrame(BufferedImage Frame) {
Frames.add(Frame);
}
public BufferedImage getNextFrame() {
if (currentFrame == Frames.size()) {
currentFrame = 0;
}
return Frames.get(currentFrame++);
}
}
You can create a class representing each enemy and a hitbox for each enemy, an area in which the enemy is in and if the turret projectile enters that area the enemy gets damaged. This could be e.g. a rectangle or a circle and the hitbox's coordinates should update every time the enemy moves.
What you're doing is great in order to get some knowledge on fundamental stuff but if you want to do something really good you should use existing frameworks.

Placing Fans in frames

import java.awt.*;
import javax.swing.JPanel;
public class FigurePanel extends JPanel {
// Define constants
public static final int LINE = 1;
public static final int RECTANGLE = 2;
public static final int ROUND_RECTANGLE = 3;
public static final int OVAL = 4;
public static final int ARC = 5;
public static final int POLYGON = 6;
private int type = 1;
private boolean filled;
/** Construct a default FigurePanel */
public FigurePanel() {
}
/** Construct a FigurePanel with the specified type */
public FigurePanel(int type) {
this.type = type;
}
/** Construct a FigurePanel with the specified type and filled */
public FigurePanel(int type, boolean filled) {
this.type = type;
this.filled = filled;
}
/** Draw a figure on the panel */
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Get the appropriate size for the figure
int width = getSize().width;
int height = getSize().height;
switch (type) {
case LINE: // Display two cross lines
g.setColor(Color.BLACK);
g.drawLine(10, 10, width - 10, height - 10);
g.drawLine(width - 10, 10, 10, height - 10);
break;
case RECTANGLE: // Display a rectangle
g.setColor(Color.BLUE);
if (filled)
g.fillRect((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height));
else
g.drawRect((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height));
break;
case ROUND_RECTANGLE: // Display a round-cornered rectangle
g.setColor(Color.RED);
if (filled)
g.fillRoundRect((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height), 20, 20);
else
g.drawRoundRect((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height), 20, 20);
break;
case OVAL: // Display an oval
g.setColor(Color.BLACK);
if (filled)
g.fillOval((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height));
else
g.drawOval((int)(0.1 * width), (int)(0.1 * height),
(int)(0.8 * width), (int)(0.8 * height));
break;
case ARC: // Display an arc
g.setColor(Color.BLACK);
if (filled) {
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius =
(int)(Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
g.fillArc(x, y, 2 * radius, 2 * radius, 0, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 90, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 180, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 270, 30);
}
else {
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius =
(int)(Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
g.drawArc(x, y, 2 * radius, 2 * radius, 0, 30);
g.drawArc(x, y, 2 * radius, 2 * radius, 90, 30);
g.drawArc(x, y, 2 * radius, 2 * radius, 180, 30);
g.drawArc(x, y, 2 * radius, 2 * radius, 270, 30);
};
break;
case POLYGON: // Display a polygon
g.setColor(Color.BLACK);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius =
(int)(Math.min(getWidth(), getHeight()) * 0.4);
// Create a Polygon object
Polygon polygon = new Polygon();
// Add points to the polygon
polygon.addPoint(xCenter + radius, yCenter);
polygon.addPoint((int)(xCenter + radius *
Math.cos(2 * Math.PI / 6)), (int)(yCenter - radius *
Math.sin(2 * Math.PI / 6)));
polygon.addPoint((int)(xCenter + radius *
Math.cos(2 * 2 * Math.PI / 6)), (int)(yCenter - radius *
Math.sin(2 * 2 * Math.PI / 6)));
polygon.addPoint((int)(xCenter + radius *
Math.cos(3 * 2 * Math.PI / 6)), (int)(yCenter - radius *
Math.sin(3 * 2 * Math.PI / 6)));
polygon.addPoint((int)(xCenter + radius *
Math.cos(4 * 2 * Math.PI / 6)), (int)(yCenter - radius *
Math.sin(4 * 2 * Math.PI / 6)));
polygon.addPoint((int)(xCenter + radius *
Math.cos(5 * 2 * Math.PI / 6)), (int)(yCenter - radius *
Math.sin(5 * 2 * Math.PI / 6)));
// Draw the polygon
if (filled)
g.fillPolygon(polygon);
else
g.drawPolygon(polygon);
}
}
/** Set a new figure type */
public void setType(int type) {
this.type = type;
// repaint();
}
/** Return figure type */
public int getType() {
return type;
}
/** Set a new filled property */
public void setFilled(boolean filled) {
this.filled = filled;
repaint();
}
/** Check if the figure is filled */
public boolean isFilled() {
return filled;
}
/** Specify preferred size */
public Dimension getPreferredSize() {
return new Dimension(80, 80);
}
}
^ That is Figure Panel class
import java.awt.*;
import javax.swing.*;
public class fan extends JFrame {
public static void main(String[] args) {
JFrame frame = new fan();
frame.setSize(300, 300);
frame.setTitle("Exercise13_09");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
public fan() {
setLayout(new GridLayout(2, 2));
add(new FigurePanel(FigurePanel.ARC, true));
add(new FigurePanel(FigurePanel.OVAL));
add(new FigurePanel(FigurePanel.ARC, true));
add(new FigurePanel(FigurePanel.OVAL, true));
}
}
This is the fan class
I'm currently trying to make a picture of a fan to show up on the screen and was wondering if there was a way to make it draw both the arc and oval in the same spot? I have been messing around with the Figure panel class but I'm not quite sure how I could merge ARC true with OVAL. Help would be appreciated thank you
Add drawOval() at the end of the fill branch of case ARC:
g.drawOval(x, y, 2 * radius, 2 * radius);
Also consider RenderingHints:
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

Categories

Resources