NetBeans项目

游戏物理演示赢博体育程序

游戏物理演示赢博体育程序是一个JavaFX赢博体育程序,它运行一个简单的模拟球在盒子里弹来弹去。

赢博体育程序响应关键事件,并允许玩家使用箭头键移动内部框。每当球击中内部盒子或外部墙壁时,它都会以现实的方式反弹。

仿真和仿真类

在这个赢博体育程序中实现模拟球的弹跳的主要类是仿真类。

public class Simulation {private Box outer;私人球球;private Box内部;private Lock锁;public Simulation(int width,int height,int dX,int dY) {outer = new Box(0,0,width,height,false);ball = new ball (width/2,height/2,dX,dY);inner = new Box(width - 60,height - 40,40,20,true);lock = new ReentrantLock();} //通过一轮演进模拟,以时间为单位推进模拟。public void evolve(double time){} //将内框移动指定的量public void moveInner(int deltaX,int deltaY){} //设置要在GUI公共列表中显示的形状<Shape> setUpShapes() {ArrayList<Shape> newShapes = new ArrayList<Shape>();newShapes.add (outer.getShape ());newShapes.add (inner.getShape ());newShapes.add (ball.getShape ());返回newShapes;} //通过将物体移动到正确的位置来更新GUI形状公共void updateShapes() {inner.updateShape();ball.updateShape ();}}

模拟容器首先是一组模拟对象的容器:外部盒子(形成模拟的墙壁),内部盒子(用户可以通过按箭头键移动)和球(将在模拟世界中移动)。

两个盒子和球在以下两个类中实现。

public class Box {private ArrayList< linesegement > walls;private矩形r;Public int x;Public int;Public int width;Public int height;//如果你想要一个向外指向法线的盒子,将外向设置为true public box (int x,int y,int width,int height,boolean外向){}public Ray bounceRay(Ray in,double time) {} public void move(int deltaX,int deltaY) {} public boolean contains(Point p) {} public Shape getShape() {r = new Rectangle(x, y, width, height);r.setFill (Color.WHITE);r.setStroke (Color.BLACK);返回r;}公共无效updateShape() {r.s setx (x);r.setY (y);}}公共类球{私有射线r;私人圈子c;public Ball(int startX,int startY,int dX,int dY) {} public Ray getRay(){返回r;}公共无效settray (Ray r){这。R = R;} public void move(double time) {r = new Ray(r. endpoint (time),r.v,r.speed);}公共Shape getShape() {c = new Circle(r.origin.x,r.origin.y,4);c.setFill (Color.RED);返回c;}公共无效updateShape() {c.setCenterX(r.r original .x);c.setCenterY (r.origin.y);}}

这两个类都包含两种表示。其中第一个是内部表示,物理模拟代码将使用它来运行模拟。在Box的情况下,内部表示是linesegement对象的列表。在球的例子中,内部表示是一个射线对象。这些类包含的第二种表示是GUI将用来表示屏幕上对象的外部表示。这些表示就是简单的JavaFX形状对象。在Box的情况下,该形状是矩形。在球的例子中,这个形状是圆的。

这两个类都具有getShape()方法,该方法从内部表示中的数据构造外部表示。这两个类还包含一个updateShape()方法,该方法从内部表示读取数据以更新外部表示。这个方法将在每一轮模拟中被调用。

物理课

为了实现模拟的运动物理,该程序将使用一组四个类来表示模拟中的关键概念。

第一个是一个简单的Point类:

public class Point {public double x;Public双y;public Point(双x,双y) {this。X = X;这一点。Y = Y;}}

第二个类是Vector类,它包含实现常见Vector操作的方法:

公共类向量{公共双dX;公共双dY;公共向量(双dX,双dY){这个。dX = dX;这一点。dY = dY;}公共void normalize() {double length = this.length();dX /=长度;dY /=长度;} public double length(){返回Math.sqrt(dotProduct(this,this));} public double crossProduct(Vector 1,Vector 2){返回one. dx *two. dy -two. dx *one. dy;} static public double dotProduct(Vector 1,Vector 2){返回1 . dx * 2。dX + 1 dy * 2 dy;}}

第三节物理课是Ray的课。射线是一个原点和一个方向向量的组合。方向矢量是两个元素的组合:一个单位矢量v指定光线的方向,一个速度值给出方向矢量的长度。

公共类射线{公共点原点;公共向量v;公共双速;公共射线(点原点,向量v,双倍速度){这。Origin = Origin;这一点。V = V;this.v.normalize ();这一点。Speed =速度;} //构造并返回一个表示路径的线段//对象将占用给定的时间跨度。public LineSegment toSegment(double time){返回新的LineSegment(origin,this.endPoint(time));} //计算给定时间跨度之后的位置。公共点端点(双时间){双destX =原点。x + v.dX*时间*速度;双y =原点。y + v.dY*时间*速度;return new Point(destX,destY);} //计算并返回这条射线最接近给定点的时间。public double getTime(Point p) {if(Math.abs(v.dX) > Math.abs(v.dY)) return (p.x - origin.x)/(v.dX*speed);return (pv - origin.y)/(pv *speed);}}

最后一个物理类是LineSegment,它代表一个简单的线段,有两个端点。LineSegment类包含有用的计算方法来回答涉及线段的关键问题,例如两条线段是否相交,以及当光线与线段碰撞并反弹时会发生什么。

/* LineSegment类表示有向线段和附加的*法线。线段的方向是一个从点* a到点b的矢量。法线是一个单位矢量,从像素坐标空间的方向矢量顺时针旋转90度*。*/公共类linessegment{公共点a;公共点b;公共LineSegment(点a,点b){这。A = A;这一点。B = B;}公共无效移动(int deltaX,int deltaX) {a.x += deltaX;a.y += deltaY;b.x += deltaX;b.y += deltaY;}公共Vector toVector(){返回新Vector(b。X - a, X,b。Y - a.y);} //返回这两个线段相交的点,//如果不相交则返回null。public Point intersection(LineSegment other){//用于计算交集的技术描述在// http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect LineSegment connector = new LineSegment(this.a,other.a);Vector = this.toVector();Vector = other.toVector();Vector = connector.toVector();double common = vector .cross - product (me,them);//如果叉积为正,则另一条线段来自我们的右侧,我们不应该让它与我们相交。如果(common >= 0.0)返回null;double t = Vector.crossProduct(us,them)/common;double u = Vector.crossProduct(us,me)/common;如果(0 < = t t < = 1 & & & & 0 < = u & & < = 1){返回新的点(一个。x +我,dx *t,a。y + me. y *t);}返回null;} //返回一个代表其他线段反射的射线。为了使其正确工作,另一个线段的矢量需要//与法线方向相反。//返回的光线位于//交点处,朝向反射方向。public Ray reflect(LineSegment other,double speed){//计算该线段的法线向量N = new Vector(a.y- b.b y,b.x-a.x);N.normalize ();//计算反射方向V = other.toVector();double dot = Vector.dotProduct(N,V);If (dot > 0) dot = -dot;向量R =新向量(V)dX - 2*•* n, dX V。dY - 2*dot*N.dY);//构造并返回结果Ray Point origin = this.intersection(other);返回新射线(原点,R,速度);}}

这四个类必须执行的最重要的工作是模拟球在虚拟空间中的运动。在每一轮的模拟中,我们都要从计算球的轨迹开始。给定描述球运动的射线,我们可以问,经过一段很短的时间后,射线的原点会移动到哪里。Ray类的toSegment()方法执行此计算并以LineSegment的形式返回轨迹,其中一个端点是对象开始的位置,另一个端点是对象结束的位置。

下一步是确定这条轨迹是否会在任何一点上与构成外框或内框的线段相交。LineSegment类有一个intersection()方法可以回答这个问题。如果球的轨迹与LineSegment相交,intersection()将返回发生交集的确切点。如果有碰撞,LineSegment的reflect()方法将计算由碰撞产生的新光线。

设置和运行模拟

现在我们已经有了一组可以为我们实现模拟的类,我们所需要的就是一个实际运行模拟的线程。

该线程在JavaFX赢博体育程序的start方法中设置。下面是设置和启动线程的代码:

new Thread(() -> {while (true) {sim.evolve(1.0);Platform.runLater (() - > sim.updateShapes ());try {Thread.sleep(50);} catch (InterruptedException ex) {}}}).start();

响应关键事件

物理演示赢博体育程序还响应关键事件。当模拟运行时,用户可以通过按箭头键移动内框。

设置键处理的代码也出现在赢博体育程序的start方法中,作为设置模拟和用于显示模拟的窗格的代码的一部分:

GamePane root = new GamePane();Simulation sim = new Simulation(300, 250, 2,2);root.setShapes (sim.setUpShapes ());场景场景=新场景(根,300,250);根。setOnKeyPressed(e -> {switch (e.getCode()) {case DOWN: sim。moveInner (0, 3);打破;case UP: sim。moveInner (0, 3);打破;左:sim。moveInner (3,0);打破;右:sim。moveInner (3,0);打破;}});root.requestFocus ();