Skip to content

Commit d475111

Browse files
committed
init
0 parents  commit d475111

14 files changed

+918
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.class

BoxCollider.java

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import java.awt.*;
2+
3+
public class BoxCollider extends Collider
4+
{
5+
// amount to overshoot for determining the collision direction
6+
final int DIRECTION_FORGIVENESS = 2;
7+
8+
double width;
9+
double height;
10+
double degrees;
11+
12+
// local points
13+
Point localTL;
14+
Point localTR;
15+
Point localBL;
16+
Point localBR;
17+
18+
// create a new box collider
19+
public BoxCollider(double x, double y, double width, double height, double degrees)
20+
{
21+
this.x = x;
22+
this.y = y;
23+
this.width = width;
24+
this.height = height;
25+
this.degrees = degrees;
26+
27+
calculateBounds();
28+
}
29+
30+
// calculate the local points for each vertex
31+
public void calculateBounds()
32+
{
33+
localTL = PointMath.transformPoint(x, y, (x - width / 2), (y + height / 2), degrees);
34+
localTR = PointMath.transformPoint(x, y, (x + width / 2), (y + height / 2), degrees);
35+
localBL = PointMath.transformPoint(x, y, (x - width / 2), (y - height / 2), degrees);
36+
localBR = PointMath.transformPoint(x, y, (x + width / 2), (y - height / 2), degrees);
37+
}
38+
39+
// draw the box collider on the screen
40+
public void draw(Graphics g)
41+
{
42+
g.drawLine(localTL.getIntX(), localTL.getIntY(), localTR.getIntX(), localTR.getIntY());
43+
g.drawLine(localTR.getIntX(), localTR.getIntY(), localBR.getIntX(), localBR.getIntY());
44+
g.drawLine(localBR.getIntX(), localBR.getIntY(), localBL.getIntX(), localBL.getIntY());
45+
g.drawLine(localBL.getIntX(), localBL.getIntY(), localTL.getIntX(), localTL.getIntY());
46+
}
47+
48+
// returns whether or not a point exists inside the collider bounds
49+
public boolean intersect(double x, double y)
50+
{
51+
Point localPoint = worldToLocal(x, y);
52+
return localPoint.x < this.x + width / 2 &&
53+
localPoint.x > this.x - width / 2 &&
54+
localPoint.y < this.y + height / 2 &&
55+
localPoint.y > this.y - height / 2;
56+
}
57+
58+
// this means a node has collided with the collider, so now we have to rebound it,
59+
// basically bouncing it off of the surface of this collider
60+
@Override
61+
public void rebound(Node node)
62+
{
63+
if(listener != null)
64+
{
65+
listener.onCollisionTriggered(this, node);
66+
return;
67+
}
68+
69+
// convert the node's position and oldPosition from world space
70+
// to local space, which will make calculations easier
71+
Point pos = worldToLocal(node.x, node.y);
72+
Point oldPos = worldToLocal(node.oldX, node.oldY);
73+
74+
double velX = pos.x - oldPos.x;
75+
double velY = pos.y - oldPos.y;
76+
77+
// if it's rebounding, it means the node has intersected THIS TICK.
78+
// meaning the old position will still be outside this collider.
79+
// therefore, we can use the old position to check which direction it entered from.
80+
81+
// calculate the bounds of this collider
82+
double top = this.y - height / 2;
83+
double bottom = this.y + height / 2;
84+
double left = this.x - width / 2;
85+
double right = this.x + width / 2;
86+
87+
// store the position variables to modify later
88+
double newX = pos.x;
89+
double newY = pos.y;
90+
91+
double newOldX = oldPos.x;
92+
double newOldY = oldPos.y;
93+
94+
// check all potential collision directions, and apply force as needed to rebound the node
95+
if(oldPos.y > bottom - DIRECTION_FORGIVENESS) // below
96+
{
97+
newY = bottom;
98+
newOldY = bottom + velY * Simulation.COLLISION_DAMP;
99+
}
100+
else if(oldPos.y < top + DIRECTION_FORGIVENESS) // above
101+
{
102+
newY = top;
103+
newOldY = top + velY * Simulation.COLLISION_DAMP;
104+
}
105+
else if(oldPos.x > right - DIRECTION_FORGIVENESS) // right
106+
{
107+
newX = right;
108+
newOldX = right + velX * Simulation.COLLISION_DAMP;
109+
}
110+
else if(oldPos.x < left + DIRECTION_FORGIVENESS) // left
111+
{
112+
newX = left;
113+
newOldX = left + velX * Simulation.COLLISION_DAMP;
114+
}
115+
116+
// convert positions from local space to world space so we can apply them to the node
117+
Point newPos = localToWorld(newX, newY);
118+
Point newOldPos = localToWorld(newOldX, newOldY);
119+
120+
// apply the new positions to the node
121+
node.x = newPos.x;
122+
node.y = newPos.y;
123+
node.oldX = newOldPos.x;
124+
node.oldY = newOldPos.y;
125+
}
126+
127+
// convert a point from world to local space
128+
Point worldToLocal(double x, double y)
129+
{
130+
return PointMath.transformPoint(this.x, this.y, x, y, -degrees);
131+
}
132+
133+
// convert a point from local to world space
134+
Point localToWorld(double x, double y)
135+
{
136+
return PointMath.transformPoint(this.x, this.y, x, y, degrees);
137+
}
138+
}

CircleCollider.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import java.awt.*;
2+
3+
public class CircleCollider extends Collider
4+
{
5+
int radius;
6+
7+
// create a new circle collider
8+
public CircleCollider(double x, double y, int radius)
9+
{
10+
this.x = x;
11+
this.y = y;
12+
this.radius = radius;
13+
}
14+
15+
// draw the box collider on the screen
16+
public void draw(Graphics g)
17+
{
18+
g.setColor(Color.BLUE);
19+
g.drawOval(getIntX() - radius, getIntY() - radius, radius * 2, radius * 2);
20+
g.fillOval(getIntX() - 2, getIntY() - 2, 4, 4);
21+
}
22+
23+
// returns whether or not a point exists inside the collider bounds
24+
public boolean intersect(double x, double y)
25+
{
26+
return PointMath.distance(this.x, this.y, x, y) <= radius;
27+
}
28+
29+
// this means a node has collided with the collider, so now we have to rebound it,
30+
// basically bouncing it off of the surface of this collider
31+
@Override
32+
public void rebound(Node node)
33+
{
34+
if(listener != null)
35+
{
36+
listener.onCollisionTriggered(this, node);
37+
return;
38+
}
39+
40+
// get the direction the node is coming from
41+
double dirX = node.x - x;
42+
double dirY = node.y - y;
43+
44+
// get the velocityu of the node
45+
double velX = node.x - node.oldX;
46+
double velY = node.y - node.oldY;
47+
48+
// get the magnitude of the velocity
49+
double velocityMagnitude = PointMath.magnitude(velX, velY);
50+
51+
// get the magnitude of the direction (which in this case is just the distance to the node)
52+
double magnitude = PointMath.magnitude(dirX, dirY);
53+
54+
// normalize the x and y of the distance to the node, which gives us a direction vector
55+
double normalizedX = dirX / magnitude;
56+
double normalizedY = dirY / magnitude;
57+
58+
// move the node to the edge of the collider based on the direction vector
59+
double pushedX = normalizedX * radius;
60+
double pushedY = normalizedY * radius;
61+
62+
// add force to the node by setting the old position further towards the center of
63+
// the collider depending on the node's current velocity. this essentially inverts the
64+
// velocity of the node (while taking some for collision dampening)
65+
double pushedOldX = normalizedX * (radius - velocityMagnitude * Simulation.COLLISION_DAMP);
66+
double pushedOldY = normalizedY * (radius - velocityMagnitude * Simulation.COLLISION_DAMP);
67+
68+
// apply the new positions
69+
node.x = x + pushedX;
70+
node.y = y + pushedY;
71+
72+
node.oldX = x + pushedOldX;
73+
node.oldY = y + pushedOldY;
74+
}
75+
}

Collider.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import java.awt.*;
2+
3+
public abstract class Collider
4+
{
5+
// the position of the collider
6+
public double x;
7+
public double y;
8+
9+
// returns whether or not a point exists inside the collider bounds
10+
abstract boolean intersect(double x, double y);
11+
12+
// draw the box collider on the screen
13+
abstract void draw(Graphics g);
14+
15+
// this means a node has collided with the collider, so now we have to rebound it,
16+
// basically bouncing it off of the surface of this collider
17+
abstract void rebound(Node node);
18+
19+
// get a rounded version of the x
20+
public int getIntX()
21+
{
22+
return (int)Math.round(x);
23+
}
24+
25+
// get a rounded version of the y
26+
public int getIntY()
27+
{
28+
return (int)Math.round(y);
29+
}
30+
31+
CollisionTriggerListener listener;
32+
33+
public void addTriggerListener(CollisionTriggerListener listener)
34+
{
35+
this.listener = listener;
36+
}
37+
}

CollisionTriggerListener.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public interface CollisionTriggerListener
2+
{
3+
public void onCollisionTriggered(Collider col, Node node);
4+
}

Constraint.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import java.awt.*;
2+
3+
public class Constraint
4+
{
5+
// the nodes that the constraint is attached to
6+
public Node nodeA;
7+
public Node nodeB;
8+
9+
// the initial length of the constraint
10+
public double length;
11+
12+
// create a new constraint
13+
public Constraint(Node a, Node b)
14+
{
15+
this.nodeA = a;
16+
this.nodeB = b;
17+
18+
// initialize the length to the distance between the nodes
19+
length = PointMath.distance(a.x, a.y, b.x, b.y);
20+
}
21+
22+
// draw the constraint on the screen
23+
public void draw(Graphics g)
24+
{
25+
g.drawLine((int)Math.round(nodeA.x), (int)Math.round(nodeA.y), (int)Math.round(nodeB.x), (int)Math.round(nodeB.y));
26+
}
27+
28+
// Get a color corresponding to a stress percentage (0-1)
29+
/*Color getStressColor(double percent)
30+
{
31+
double inversePercent = 1 - percent;
32+
33+
Color a = Color.RED;
34+
Color b = Color.GREEN;
35+
36+
double red = a.getRed() * percent + b.getRed() * inversePercent;
37+
double green = a.getGreen() * percent + b.getGreen() * inversePercent;
38+
double blue = a.getBlue() * percent + b.getBlue() * inversePercent;
39+
40+
Color result = new Color((int)Math.round(red), (int)Math.round(green), (int)Math.round(blue));
41+
42+
return result;
43+
}*/
44+
}

Game.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import java.util.*;
2+
3+
public class Game extends Simulation implements CollisionTriggerListener
4+
{
5+
static final int WIDTH = 1500;
6+
static final int HEIGHT = 750;
7+
8+
public static void main(String[] args) throws InterruptedException
9+
{
10+
Game app = new Game();
11+
}
12+
13+
public Game() throws InterruptedException
14+
{
15+
super(WIDTH, HEIGHT);
16+
17+
Random random = new Random();
18+
19+
for(int x = 100; x < WIDTH - 100; x += 1)
20+
{
21+
Node node = new Node(x + randomInt(random, -10, 10), 100 + randomInt(random, -90, 0));
22+
nodes.add(node);
23+
}
24+
25+
for(int x = 0; x < WIDTH; x += 25)
26+
{
27+
for(int y = 200; y < HEIGHT - 100; y += 25)
28+
{
29+
Collider col = new CircleCollider(x + (y % 2 == 0 ? 0 : 12) + randomInt(random, -5, 5), y, randomInt(random, 3, 14));
30+
colliders.add(col);
31+
}
32+
}
33+
34+
Collider bottom = new BoxCollider(WIDTH / 2, HEIGHT - 5, WIDTH, 10, 0);
35+
bottom.addTriggerListener(this);
36+
colliders.add(bottom);
37+
38+
// main simulation loop
39+
while(true)
40+
{
41+
simulate();
42+
repaint();
43+
Thread.sleep(5);
44+
}
45+
}
46+
47+
public void onCollisionTriggered(Collider col, Node node)
48+
{
49+
double vel = node.oldY - node.y;
50+
node.y = 5;
51+
node.oldY = node.y + vel;
52+
}
53+
54+
// returns a random integer between the min and the max based on a seed (exclusive)
55+
public static int randomInt(Random random, int min, int max)
56+
{
57+
return random.nextInt(max - min) + min;
58+
}
59+
}

0 commit comments

Comments
 (0)