(c)도경구
version 0.5 (2022/10/07) 실습 #6-1, #6-2 완성 코드
version 0.6 (2022/10/09) 실습 #6-3 완성 코드 추가
version 1.0 (2022/10/13) 숙제 완성 코드 추가
6. 실습 - 완성 코드
상자 속 공 굴리기 애니메이션 (확장)
실습#6-1. 파란 공을 하나 추가
AnimationWriter
클래스를 두 개의 공을 그리는 BallWriter
객체를 별로로 만들어 운영
import javax.swing.*;
import java.awt.*;
public class AnimationWriter extends JPanel {
private BoxWriter box_writer;
private BallWriter ball_writer1;
private BallWriter ball_writer2;
public AnimationWriter(BoxWriter box, BallWriter ball1, BallWriter ball2, int size) {
box_writer = box;
ball_writer1 = ball1;
ball_writer2 = ball2;
JFrame f = new JFrame();
f.getContentPane().add(this);
f.setTitle("Bounce");
f.setSize(size+3, size+30);
f.setVisible(true);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void paintComponent(Graphics g) {
box_writer.paintComponent(g);
ball_writer1.paintComponent(g);
ball_writer2.paintComponent(g);
}
}
BounceController
클래스를 두 개의 공을 제어하도록 수정
public class BounceController {
private MovingBall ball1;
private MovingBall ball2;
private AnimationWriter writer;
public BounceController(MovingBall b1, MovingBall b2, AnimationWriter w) {
ball1 = b1;
ball2 = b2;
writer = w;
}
public void runAnimation() {
while (true) {
delay(20);
ball1.move(1);
ball2.move(1);
writer.repaint();
}
}
/** delay - how_long millisecond 동안 실행 정지 */
private void delay(int how_long) {
try { Thread.sleep(how_long); }
catch (InterruptedException e) { }
}
}
BounceTheBall
클래스 수정
import java.awt.Color;
public class BounceTheBall {
public static void main(String[] args) {
int size = 200;
Box box = new Box(size);
MovingBall ball1 = new MovingBall(30,30,6,box);
MovingBall ball2 = new MovingBall(180,120,6,box);
BoxWriter box_writer = new BoxWriter(size);
BallWriter ball_writer1 = new BallWriter(ball1,Color.RED);
BallWriter ball_writer2 = new BallWriter(ball2,Color.BLUE);
AnimationWriter writer = new AnimationWriter(box_writer,
ball_writer1,
ball_writer2,
size);
new BounceController(ball1,ball2,writer).runAnimation();
}
}
실습#6-2. 충돌시 진로 수정
MovingBall
클래스에 진로 수정 메소드 reverse()
추가
public void reverse() {
x_velocity = - x_velocity;
y_velocity = - y_velocity;
}
BounceController
클래스의 runAnimation()
수정
public void runAnimation() {
while (true) {
delay(20);
ball1.move(1);
ball2.move(1);
if (collide(ball1,ball2)) {
ball1.reverse();
ball2.reverse();
}
writer.repaint();
}
}
private boolean collide(MovingBall b1, MovingBall b2) {
int dist1 = b1.x_pos() - b2.x_pos();
int dist2 = b1.y_pos() - b2.y_pos();
double distance = Math.sqrt(Math.pow(dist1,2) + Math.pow(dist2,2));
return distance <= b1.radius() + b2.radius();
}
실습#6-3. 장애물 설치
- 중앙에 다음과 같은 모양의 적당한 길이의 장애물을 설치한다. (색깔은 자유 선택)
- 공이 이 장애물 위면 또는 아래 면을 만나면 y축 진행 방향을 바꾼다.
Box
클래스에 장애물 정보를 담은 필드 변수 추가
private final int BOX_SIZE;
private final int OBSTACLE_WIDTH;
private final int OBSTACLE_X0;
public Box(int n) {
BOX_SIZE = n;
int width = n / 4;
OBSTACLE_WIDTH = width;
OBSTACLE_X0 = (n - width) / 2;
}
public int box_size() { return BOX_SIZE; }
public int obstacleWidth() { return OBSTACLE_WIDTH; }
public int obstacleX0() { return OBSTACLE_X0; }
Box
클래스에 장애물 접촉 여부를 알려주는 메소드 추가
public boolean inObstacleContact(int x, int y) {
return OBSTACLE_X0 <= x && x <= OBSTACLE_X0 + OBSTACLE_WIDTH && y == BOX_SIZE / 2;
}
BoxWriter
클래스의 paintComponent
메소드에 장애물 그리는 코드 추가
public void paintComponent(Graphics g) {
int size = box.box_size();
g.setColor(Color.WHITE);
g.fillRect(0, 0, size, size);
g.setColor(Color.BLACK);
g.drawRect(0, 0, size, size);
g.setColor(Color.GRAY);
int x = box.obstacleX0();
int y = size / 2 - size / 80;
int width = box.obstacleWidth();
int height = size / 40;
g.fillRect(x, y, width, height);
}
MovingBall
클래스의 move
메소드에 장애물을 만났을 때 y축의 진행 방향을 바꾸는 코드 추가
public void move(int time_units) {
x_pos += x_velocity;
if (container.inHorizontalContact(x_pos))
x_velocity = - x_velocity;
y_pos += y_velocity;
if (container.inVerticalContact(y_pos))
y_velocity = - y_velocity;
if (container.inObstacleContact(x_pos, y_pos))
y_velocity = - y_velocity;
}
6. 숙제 - 완성 코드
import javax.swing.*;
import java.awt.*;
import java.time.*;
public class ClockWriter extends JPanel {
private final int SIZE;
public ClockWriter(int size) {
SIZE = size;
JFrame frame = new JFrame();
frame.setTitle("Clock");
frame.setSize(SIZE+50, SIZE+110);
frame.getContentPane().add(this);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void paintComponent(Graphics g) {
// 현재시간 알아보기
LocalTime now = LocalTime.now();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
// 디지털 시계
g.setColor(Color.BLACK);
String clock = format2(hour) + ":" + format2(minute) + ":" + format2(second);
g.drawString(clock, 116, 35);
// 아날로그 시계
g.setColor(Color.LIGHT_GRAY);
g.fillOval(25, 60, SIZE, SIZE);
// 시계 중심
int radius = SIZE / 2;
int x1 = 25 + radius;
int y1 = 60 + radius;
// 초침 그리기
int diameter = (second == 0) ? SIZE : SIZE / 60 * second;
int base = (SIZE - diameter) / 2;
g.setColor(Color.PINK);
g.fillOval(25+base, 60+base, diameter, diameter);
// 분침 그리기
radius -= 30;
double minute_angle = (minute - 15) * Math.PI / 30;
int x2 = x1 + (int)(radius * Math.cos(minute_angle));
int y2 = y1 + (int)(radius * Math.sin(minute_angle));
g.setColor(Color.RED);
g.drawLine(x1, y1, x2, y2);
// 시침 그리기
radius -= 30;
double hour_angle = (hour - 3) * Math.PI / 6 + minute_angle / 12;
x2 = x1 + (int)(radius * Math.cos(hour_angle));
y2 = y1 + (int)(radius * Math.sin(hour_angle));
g.setColor(Color.YELLOW);
g.drawLine(x1, y1, x2, y2);
}
/** format2 - 1자리 정수이면 앞에 0을 붙여 문자열로 만들어주는 메소 */
private String format2(int number) {
if (number < 10)
return "0" + number;
else
return "" + number;
}
}