【Java】多角形クラスと矩形との当たり判定

2018年1月3日

本稿はJavaのPolygonクラスについて紹介します。

Polygonクラスは多角形を表すオブジェクトで、それに関連するいろいろなメソッドを利用できます。

オブジェクトの作成

オブジェクトの作成は
new Polygon(x座標配列,y座標配列,点の数)
で定義します。

各x座標とy座標は配列の添え字番号で一致しています。

例えば、x座標配列が[100,50,3000]でy座標配列が[200,400,600]とした場合は点(100,200)と点(50,400)と点(3000,600)を直線で結んだ図形が作成されます。

drawPolygonメソッドと同じ要領ですね。

  1. import java.awt.Polygon;
  2. import java.util.Arrays;
  3. public class Test {
  4. public static void main(String[] args) {
  5. Polygon po = new Polygon(new int[] {100,150,200}, new int[] {150,100,150}, 3);
  6. System.out.println(Arrays.toString(po.xpoints));
  7. System.out.println(Arrays.toString(po.ypoints));
  8. }
  9. }
実行結果

[100, 150, 200]
[150, 100, 150]

translateメソッド

現在の座標から指定した座標分移動させることができます。

  1. import java.awt.Polygon;
  2. import java.util.Arrays;
  3. public class Test {
  4. public static void main(String[] args) {
  5. Polygon po = new Polygon(new int[] {100,150,200}, new int[] {150,100,150}, 3);
  6. po.translate(50, 50);
  7. System.out.println(Arrays.toString(po.xpoints));
  8. System.out.println(Arrays.toString(po.ypoints));
  9. }
  10. }
実行結果

[150, 200, 250]
[200, 150, 200]

containsメソッド

多角形内に指定された座標が含まれているかを判定します。

contains(x座標,y座標)と定義しましょう。

点と多角形の当たり判定で利用できます。

  1. import java.awt.Polygon;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Polygon po = new Polygon(new int[] {100,150,200}, new int[] {150,100,150}, 3);
  5. System.out.println(po.contains(150, 130));
  6. }
  7. }
実行結果

true

intersectsメソッド

intersectsメソッドを利用することで、多角形と矩形の当たり判定が実装できます。

Polygonオブジェクト.intesects(Rectangleオブジェクト)と定義しましょう。

それではサンプルをご覧ください。

  1. import java.awt.Polygon;
  2. import java.awt.Rectangle;
  3.  
  4. public class Test {
  5. public static void main(String[] args) {
  6. Polygon r1 = new Polygon(new int[] {100,150,200}, new int[] {150,100,150}, 3);
  7. Rectangle r2 = new Rectangle(100, 100, 50, 50);
  8. System.out.println(r1.intersects(r2));
  9. }
  10. }
実行結果

true

多角形と矩形をアニメーションさせながら判定できているか確かめる

実際にアニメーションサンプルを実装して判定できているか確かめてみます。

  1.  
  2. import java.awt.Color;
  3. import java.awt.Font;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Insets;
  7. import java.awt.Polygon;
  8. import java.awt.Rectangle;
  9.  
  10. import javax.swing.JFrame;
  11. import javax.swing.JPanel;
  12. public class Test {
  13. public static void main(String[] args) {
  14. TestWindow tw = new TestWindow("テスト", 400, 300);
  15. tw.change(new DrawCanvas());
  16. tw.setVisible(true);
  17. tw.startGameLoop();
  18.  
  19. }
  20. }
  21. //ウィンドウクラス
  22. class TestWindow extends JFrame implements Runnable{
  23. private Thread th = null;
  24. private double sleepAddTime;
  25. private int fps=60;
  26. public TestWindow(String title, int width, int height) {
  27. super(title);
  28. setDefaultCloseOperation(EXIT_ON_CLOSE);
  29. setSize(width,height);
  30. setLocationRelativeTo(null);
  31. setLayout(null);
  32. setResizable(false);
  33. setFps(fps);
  34. }
  35. public synchronized void change(JPanel p) {
  36. getContentPane().removeAll();
  37. Insets inset = getInsets();
  38. p.setBounds(-inset.left,-inset.top,getWidth(),getHeight());
  39. add(p);
  40. validate();
  41. repaint();
  42. }
  43. public synchronized void startGameLoop(){
  44. if ( th == null ) {
  45. th = new Thread(this);
  46. th.start();
  47. }
  48. }
  49. public synchronized void stopGameLoop(){
  50. if ( th != null ) {
  51. th = null;
  52. }
  53. }
  54. public void run(){
  55. double nextTime = System.currentTimeMillis() + sleepAddTime;
  56. while(th != null){
  57. try{
  58. long res = (long)nextTime - System.currentTimeMillis();
  59. if ( res < 0 ) res = 0;
  60. Thread.sleep(res);
  61. repaint();
  62. nextTime += sleepAddTime;
  63. }catch(InterruptedException e){
  64. e.printStackTrace();
  65. }
  66. }
  67. }
  68. public void setFps(int fps){
  69. if ( fps < 10 || fps > 60 ) {
  70. throw new IllegalArgumentException("fpsの設定は10~60の間で指定してください。");
  71. }
  72. this.fps = fps;
  73. sleepAddTime = 1000.0 / fps;
  74. }
  75. }
  76.  
  77. //移動アニメーション用クラス
  78. class VertexMove2D{
  79. private double x;
  80. private double y;
  81. private final int[] xpoints;
  82. private final int[] ypoints;
  83. private final int npoints;
  84. private boolean isInvert;
  85. private int currentPoint;
  86. private int nextPoint;
  87. private int speed;
  88. private double mx;
  89. private double my;
  90. private double checkValue;
  91. public VertexMove2D(int[] xpoints, int[] ypoints, int npoints, int speed, int initPoint) {
  92. this.xpoints = xpoints;
  93. this.ypoints = ypoints;
  94. this.npoints = npoints;
  95. this.speed = Math.abs(speed);
  96. this.isInvert = speed < 0;
  97. this.currentPoint = isInvert ? initPoint + 1 : initPoint - 1;
  98. prepareNext();
  99. }
  100. private void prepareNext() {
  101. if ( isInvert ) {
  102. int currentPoint = this.currentPoint - 1;
  103. if ( currentPoint < 0 ) currentPoint = npoints - 1;
  104. this.currentPoint = currentPoint;
  105. int nextPoint = currentPoint - 1;
  106. this.nextPoint = nextPoint < 0 ? npoints - 1 : nextPoint;
  107. } else {
  108. int currentPoint = this.currentPoint + 1;
  109. if ( currentPoint >= npoints ) currentPoint = 0;
  110. this.currentPoint = currentPoint;
  111. this.nextPoint = (currentPoint + 1) % npoints;
  112. }
  113. int sx = xpoints[currentPoint];
  114. int sy = ypoints[currentPoint];
  115. int ex = xpoints[nextPoint];
  116. int ey = ypoints[nextPoint];
  117. int vx = ex - sx;
  118. int vy = ey - sy;
  119. double rad = Math.atan2(vy, vx);
  120. mx = Math.cos(rad) * speed;
  121. my = Math.sin(rad) * speed;
  122. checkValue = vx * vx + vy * vy;
  123. x = sx;
  124. y = sy;
  125. }
  126. public void draw(Graphics g) {
  127. g.drawPolygon(xpoints, ypoints, npoints);
  128. }
  129. public void fill(Graphics g) {
  130. g.fillPolygon(xpoints, ypoints, npoints);
  131. }
  132. public void move() {
  133. double xx = x + mx;
  134. double yy = y + my;
  135.  
  136. int sx = xpoints[currentPoint];
  137. int sy = ypoints[currentPoint];
  138. double vx = sx - xx;
  139. double vy = sy - yy;
  140. if ( checkValue <= vx * vx + vy * vy ) {
  141. prepareNext();
  142. } else {
  143. x = xx;
  144. y = yy;
  145. }
  146. }
  147. public void invert() {
  148. isInvert = !isInvert;
  149. }
  150. public boolean isInvert() {
  151. return isInvert;
  152. }
  153. public int getSpeed() {
  154. return speed;
  155. }
  156. public void setSpeed(int speed) {
  157. if ( speed < 0 ) {
  158. invert();
  159. speed = -speed;
  160. }
  161. this.speed = speed;
  162. }
  163. public double getX() {
  164. return x;
  165. }
  166. public double getY() {
  167. return y;
  168. }
  169. }
  170. class DrawCanvas extends JPanel{
  171. int[] x = {60,150,280,340,270,160,90,70};
  172. int[] y = {80,200,90,120,150,250,240,260};
  173.  
  174. //図形の頂点を移動するオブジェクト。
  175. VertexMove2D m1 = new VertexMove2D(
  176. x
  177. ,y
  178. ,8//頂点の数
  179. ,2//移動速度
  180. ,0//配列の0番を開始地点に
  181. );
  182. //もう一つの図形の頂点を移動するオブジェクト。
  183. VertexMove2D m2 = new VertexMove2D(
  184. x
  185. ,y
  186. ,8//頂点の数
  187. ,-3//移動速度
  188. ,4//配列の4番を開始地点に
  189. );
  190. Polygon r1 = new Polygon(new int[] {100,150,200}, new int[] {150,100,150}, 3);
  191. Rectangle r2 = new Rectangle(100, 100, 50, 50);
  192. public void paintComponent(Graphics g){
  193. Graphics2D g2 = (Graphics2D)g;
  194. g.setColor(Color.RED);
  195. g2.draw(r1);
  196. g.setColor(Color.BLUE);
  197. g2.draw(r2);
  198. g.setColor(Color.GREEN);
  199. m1.draw(g2);
  200.  
  201. m1.move();
  202. Rectangle r1_rect = r1.getBounds();
  203. r1.translate((int)(m1.getX()-r1_rect.getCenterX()), (int)(m1.getY()-r1_rect.getCenterY()));
  204.  
  205. m2.move();
  206. r2.x = (int) (m2.getX()-r2.width / 2);
  207. r2.y = (int) (m2.getY()-r2.height / 2);
  208.  
  209. g.setFont(new Font(Font.MONOSPACED,Font.BOLD,20));
  210. if ( r1.intersects(r2) ) {
  211. g.setColor(Color.RED);
  212. g.drawString("当たっています", 50,50);
  213. } else {
  214. g.setColor(Color.BLUE);
  215. g.drawString("当たってません", 50,50);
  216. }
  217. }
  218. }
実行結果

これでちゃんと当たり判定ができていることが確認できました。