-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBall.java
More file actions
306 lines (280 loc) · 10.2 KB
/
Copy pathBall.java
File metadata and controls
306 lines (280 loc) · 10.2 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
package ball_game_readonlyLists;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
public class Ball implements Cloneable{
private int size;
private double x,y;
private double u,v;
public void setU(double u) {
this.u = u;
}
public void setV(double v) {
this.v = v;
}
public double getRadius() {
return radius;
}
public static int getSMALL() {
return SMALL;
}
public static int getMEDIUM() {
return MEDIUM;
}
public static int getLARGE() {
return LARGE;
}
private Color color;
private double diameter;
private double radius;
public Object clone() throws CloneNotSupportedException
{
return new Ball(this.size, this.x,this.y,this.u,this.v,this.color);
}
private ArrayList<Ball> collisionList;
public static int SMALL=0,MEDIUM=1,LARGE=2;
Ball(){
}
Ball(int size, double xpos, double ypos,
double xvel, double yvel, Color color){
this.size = size;
this.x = xpos;
this.y = ypos;
this.u = xvel;
this.v = yvel;
this.color = color;
if (size == SMALL)
diameter = 10.0;
else if (size == MEDIUM)
diameter = 15.0;
else if (size == LARGE)
diameter = 20.0;
collisionList = new ArrayList<Ball>(3);
radius = diameter/2;
}
public ArrayList<Ball> getCollisionList(){
return this.collisionList;
}
//only called once when game is started and its only checked by one thread so no sync needed
public void checkInitialSpawnCollision(List<Ball> ballList)
{
for (Ball b:ballList)
{
if (this == b) continue;//ignore if its 'this' ball
float bothBallRad = (float) (this.radius + b.radius);
float delX = (float) Math.abs(this.x - b.x);
float delY = (float) Math.abs(this.y - b.y);
float deltaRadius = (float) Math.sqrt(
(Math.pow(delX, 2)+(Math.pow(delY, 2))));
if (deltaRadius <= bothBallRad)
{
this.x += 20;
this.y += 20;
b.x -= 20;
b.y -= 20;
}
}
}
public void updatePosition(Rectangle2D bounds, List<Ball> threadBallList, List<Ball> ballRemList){
collisionList.clear();//clear the list
x += u;
y += v;
//check if ball hit escape window
if(((y + diameter) >= bounds.getMaxY()-40)
&& ((x + diameter) >= (4*(int)bounds.getMaxX()/10))
&& ((x - diameter) <= (5*(int)bounds.getMaxX()/10)))
{
ballRemList.add(this);
}
//check wall boundary
if (x - (diameter) <= bounds.getMinX())
{
x = bounds.getMinX() + (diameter);
u = -u;
}
if (x + (diameter) >= bounds.getMaxX())
{
x = bounds.getMaxX() - (diameter);
u = -u;
}
if (y - (diameter) <= bounds.getMinY())
{
y = bounds.getMinY() + (diameter);
v = -v;
}
if (y + (diameter) >= bounds.getMaxY()-40)
{
y = bounds.getMaxY() - (40+diameter);
v = -v;
}
}
public void checkRegularCollision(List<Ball> ballList, List<Ball> threadList, List<Ball> ballRemList, List<Ball> ballAddList)
{
try{
for (Ball b:ballList)//for the entire list of balls
{
//check for collision
if (this == b) continue;//ignore if its the same ball
float bothBallRad = (float) (this.radius + b.radius);
float delX = (float) Math.abs(this.x - b.x);
float delY = (float) Math.abs(this.y - b.y);
float deltaRadius = (float) Math.sqrt(
(Math.pow(delX, 2)+(Math.pow(delY, 2))));
if (deltaRadius < bothBallRad)//if collision detected
{
collisionList.add(b);
//before spawning new balls, i was originally checking to see
//if there are any balls near the collision so the spawned ball
//isn't created on top of another but this required checking all the
//NSEW directions and was becoming very memory intensive and making the
//program run really show
if(this.color == b.color)//if both balls are same color
sameTypeBallsCollision(b);
else if(this.size == b.size && this.color != b.color){//if same size diff color
if(b.size == 0)//both balls small
sameSizeSmallBallsCollision(b, ballAddList, threadList);//create 1 new small red ball
else if(b.size == 1)//both balls med
sameSizeMedBallsCollision(b, ballAddList, threadList);//create 2 new small red balls
else//both balls large
sameSizeLargeBallsCollision(b, ballAddList, threadList);//create 2 new small red balls and one 1 ball
}
else//bigger ball kills the smaller one
{
diffSizeAndColorBallsCollision(b, ballRemList);
}
}
}
}catch(ConcurrentModificationException e){e.printStackTrace();
}
}
private void sameTypeBallsCollision(Ball b)
{
updateBallVelocities(b);
}
private void sameSizeSmallBallsCollision(Ball b, List<Ball> ballAddList, List<Ball> threadList)
{
//create 1 new small red ball
updateBallVelocities(b);
createRedBall(ballAddList, threadList);
}
private void sameSizeMedBallsCollision(Ball b, List<Ball> ballAddList, List<Ball> threadList)
{
//create 2 new small red balls
updateBallVelocities(b);
createRedBall(ballAddList, threadList);
createRedBall(ballAddList, threadList);
}
private void sameSizeLargeBallsCollision(Ball b, List<Ball> ballAddList, List<Ball> threadList)
{
//create 2 small red balls and one blue ball
updateBallVelocities(b);
createRedBall(ballAddList, threadList);
createRedBall(ballAddList, threadList);
createBlueBall(ballAddList, threadList);
}
private void diffSizeAndColorBallsCollision(Ball b, List<Ball> ballRemList)
{
//eliminate smaller ball
if(this.size < b.size)
{
ballRemList.add(this);
}
}
private void createRedBall(List<Ball> ballAddList, List<Ball> threadList)
{
if(threadList.size() < 20)//keeping count low to avoid cluttering the window
{
Random rn = new Random();
double ranX = ((this.x-100) + (rn.nextInt(100)));
double ranY = ((this.y-100) + (rn.nextInt(100)));
double ranU = (0.5 + (rn.nextDouble()));
double ranV = (0.5 + (rn.nextDouble()));
Ball newBall = new Ball(SMALL,ranX,ranY,ranU, ranV,Color.RED);
ballAddList.add(newBall);
}
}
private void createBlueBall(List<Ball> ballAddList, List<Ball> threadList)
{
if(threadList.size() < 20)//keeping count low to avoid cluttering the window
{
Random rn = new Random();
double ranX = ((this.x-200) + (rn.nextInt(200)));
double ranY = ((this.y-200) + (rn.nextInt(200)));
double ranU = (0.5 + (rn.nextDouble()));
double ranV = (0.5 + (rn.nextDouble()));
Ball newBall = new Ball(SMALL,ranX,ranY,ranU,ranV,Color.BLUE);
ballAddList.add(newBall);
}
}
//need to use Physics to calculate momentum + energy transfer between balls
private void updateBallVelocities(Ball b)
{
//convert from cartesian to polar coordinates
double thetaVelb1 = Math.atan2(this.v, this.u);
double thetaVelb2 = Math.atan2(b.v, b.u);
//find theta to shift plane of axis to get rotated x' y' coordinates
double atan2val = Math.atan2(((this.y+this.diameter/2.0) - (b.y+b.diameter/2.0)),
((this.x+this.radius)-(b.x+b.radius)));
//find each balls x and y velocities for both balls in rotated coordinate
double b1Vel = calcVelocity(this.u, this.v);
double b2Vel = calcVelocity(b.u, b.v);
double b1VelX = b1Vel * Math.cos(thetaVelb1 - atan2val);
double b1VelY = b1Vel * Math.sin(thetaVelb1 - atan2val);
double b2VelX = b2Vel * Math.cos(thetaVelb2 - atan2val);
double b2VelY = b2Vel * Math.sin(thetaVelb2 - atan2val);
//find the final x velocities for both balls in 1D
double m1 = this.radius * this.radius;
double m2 = b.radius * b.radius;
double b1FinalVelX = (b1VelX*((double)(m1-m2)) +
((2*m2*b2VelX)))/(m1 + m2);
double b2FinalVelX = (b2VelX*((double)(m2 -m1)) +
((2*m1*b1VelX)))/(m1 + m2);
//find the magnituge velocites for both balls in cartecian coordinates
double b1FinalVel = calcVelocity(b1FinalVelX, b1VelY);
double b2FinalVel = calcVelocity(b2FinalVelX, b2VelY);
//find the theta
double b1FinalTheta = Math.atan2(b1VelY, b1FinalVelX) + atan2val;
double b2FinalTheta = Math.atan2(b2VelY, b2FinalVelX) + atan2val;
//convert back from polar to cartesian coordinates and set the new vel for both balls
double b1xVel = Math.cos(b1FinalTheta)*b1FinalVel;
double b1yVel = Math.sin(b1FinalTheta)*b1FinalVel;
// double b2xVel = Math.cos(b2FinalTheta)*b2FinalVel;
// double b2yVel = Math.sin(b2FinalTheta)*b2FinalVel;
this.u = b1xVel; this.v = b1yVel;// b.u = b2xVel; b.v = b2yVel;
}
private double calcVelocity(double u, double v)
{
return Math.sqrt((Math.pow(u, 2)+Math.pow(v, 2)));
}
public Color getColor(){
return(this.color);
}
public double getX(){
return(this.x);
}
public double getY(){
return(this.y);
}
public double getU(){
return(this.u);
}
public double getV(){
return(this.v);
}
public double getSize(){
return(this.size);
}
public double getDiameter(){
return(this.diameter);
}
public Ellipse2D getShape(){
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(x, y, x+radius, y+radius);
return circle;//new Ellipse2D.Double(x-radius,y-radius,diameter,diameter);
}
}