1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
package sample3d; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Color; import javafx.scene.text.TextAlignment; import my3d._math3d; import my3d._math3d.*; import my3d._shape3d; import my3d._viewer3d2; import my3d._viewport3d; public class _sample06 extends _viewer3d2 { public static void main(String... args) { Application.launch(args); } @Override public int get_view_width() { return 640; } @Override public int get_view_height() { return 480; } private static final double LEN = 600, WIDTH = 100, HEIGHT = 5, DEPTH = 200, BORDER = -0.75 * DEPTH, BALL_R = HEIGHT, FIELD_W = WIDTH - 2 * BALL_R, FIELD_D = DEPTH - 2 * BALL_R, RACKET_W = WIDTH / 4, RACKET_Z = BORDER - 2 * BALL_R; private _shape3d table, field, ball, racket; private _transform tf_ball, tf_racket; @Override public void init_scene(_viewport3d vp) { vp.scene_transform.position.set(0, 0, LEN); set_vert_slider_range(-90, -30); set_horz_slider_range(45, -45); table = new _shape3d(); _vector[] vertices = new _vector[16]; for (int i = 0; i < 8; i++) { vertices[i] = new _vector( (i % 4 == 0 || i % 4 == 1) ? WIDTH : -WIDTH, (i < 4) ? HEIGHT : -HEIGHT, (i % 4 == 0 || i % 4 == 3) ? DEPTH : -DEPTH); vertices[i + 8] = new _vector( (i % 4 == 0 || i % 4 == 1) ? FIELD_W : -FIELD_W, (i < 4) ? HEIGHT : -BALL_R, (i % 4 == 0 || i % 4 == 3) ? FIELD_D : -FIELD_D); } table.vertices = vertices; for (int i = 0; i < 4; i++) { int j = (i + 1) % 4; table.add_face(i, i + 4, j + 4, j); } table.add_face(0, 1, 2, 10, 9, 8); table.add_face(2, 3, 0, 8, 11, 10); table.set_color(Color.BROWN); field = new _shape3d(); field.vertices = vertices; for (int i = 8; i < 12; i++) { int j = (i + 1) % 4 + 8; field.add_face(i, j, j + 4, i + 4); } field.add_face(12, 13, 14, 15); field.set_color(Color.GREEN); ball = _shape3d.create_ball(BALL_R, 2); tf_ball = new _transform(new _vector(0, 0, BORDER)); racket = _shape3d.create_tube(BALL_R, RACKET_W, 4); tf_racket = new _transform(new _vector(0, 0, RACKET_Z)); _matrix.rot_z(Math.PI / 2, tf_racket.orientation); racket.set_color(Color.RED); _transform tf_camera = new _transform(); _vector ray = new _vector(); view.setOnMouseMoved((evt) -> { _math3d.inv(vp.scene_transform, tf_camera); _vector pos = tf_camera.position; _math3d.mul( tf_camera.orientation, vp.get_ray(evt.getX(), evt.getY(), ray), ray); double racket_x = pos.x - ray.x * pos.y / ray.y; racket_x = Math.min(racket_x, FIELD_W - RACKET_W); racket_x = Math.max(racket_x, -FIELD_W + RACKET_W); tf_racket.position.set(racket_x, 0, RACKET_Z); }); AnimationTimer timer = new AnimationTimer() { private long prev_t; @Override public void handle(long t) { if (!move_ball((t - prev_t) / 1.0e+9)) this.stop(); repaint(); prev_t = t; } }; view.setOnMouseClicked((evt) -> { timer.start(); }); } @Override public void render(_viewport3d vp) { vp.render_shape(field); vp.push_transform(tf_ball); vp.render_shape(ball); vp.pop_transform(); vp.push_transform(tf_racket); vp.render_shape(racket); vp.pop_transform(); vp.render_shape(table); } private static final double RIGHT = FIELD_W - BALL_R, LEFT = -RIGHT, BACK = FIELD_D - BALL_R, FRONT = -BACK, SPEED = 400; private double ball_x = 0, ball_z = FRONT; private double dir_x, dir_z; private int score = 0; private boolean move_ball(double dt) { if (ball_z == FRONT) { // ゲーム開始 tf_ball.position.set(ball_x = 0, 0, ball_z = BORDER); score = 0; get_move_dir(); return true; } // ゲーム中 double dv = Math.min(dt, 0.5) * SPEED; double dx = dv * dir_x, dz = dv * dir_z; double prev_dir_x = dir_x, prev_dir_z = dir_z; ball_x += dx; ball_z += dz; if (ball_x < LEFT) { ball_x = 2 * LEFT - ball_x; dir_x = -dir_x; } else if (ball_x > RIGHT) { ball_x = 2 * RIGHT - ball_x; dir_x = -dir_x; } if (ball_z > BACK) { ball_z = 2 * BACK - ball_z; dir_z = -dir_z; } else if (ball_z < BORDER) { double k = (ball_z - BORDER) / dz; if (k < 1) { double x = ball_x - dx * k; if (Math.abs(x - tf_racket.position.x) < RACKET_W) { get_move_dir(); dx = dv * dir_x; dz = dv * dir_z; ball_x = x + dx * (1 - k); ball_z = BORDER + dz * (1 - k); } } } if (prev_dir_x != dir_x || prev_dir_z != dir_z) score += 10; if (ball_z < FRONT) { ball_x -= dx / dz * (ball_z - FRONT); ball_z = FRONT; } tf_ball.position.set(ball_x, 0, ball_z); return (ball_z != FRONT); } private void get_move_dir() { double t = Math.random() * Math.PI / 4 + Math.PI / 12; if (dir_x < 0) t = -t; dir_z = Math.cos(t); dir_x = Math.sin(t); } @Override public void draw(GraphicsContext gc) { gc.setFill(Color.BLACK); gc.setTextAlign(TextAlignment.CENTER); gc.fillText(String.format("SCORE: %02d", score), get_view_width() / 2, 20); } }