-
Notifications
You must be signed in to change notification settings - Fork 0
/
PhysicsEntity.pde
119 lines (94 loc) · 3.99 KB
/
PhysicsEntity.pde
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
abstract class PhysicsEntity extends Entity {
PVector lastTickOrigin;
PVector velocity;
float radius;
float mass;
float bounce;
float roll;
Collision lastCollision;
boolean onGround;
PhysicsEntity(PVector origin, PVector velocity, float radius, float mass, float bounce, float roll) {
this.origin = origin;
this.lastTickOrigin = null;
this.velocity = velocity;
this.radius = radius;
this.mass = mass;
this.bounce = bounce;
this.roll = roll;
this.lastCollision = null;
this.onGround = false;
this.updateBoundingBox();
}
void applyForce(PVector velocity) {
this.velocity.add(velocity.copy().div(this.mass));
}
void cleanupInVoid() {
PVector lvlSize = levels.getCurrent().size;
boolean offscreenX = this.origin.x < -200 * -1 || this.origin.x > lvlSize.x + 200;
boolean offscreenY = this.origin.y < -800 || this.origin.y > lvlSize.y + 3200;
if (offscreenX || offscreenY)
this.delete();
}
void updateCollision() {
if (this.lastTickOrigin == null) return;
int collisionCount = 0;
PVector velocityNormalized = this.velocity.copy().normalize();
PVector geometryIntersection = new PVector(); // how far the circle entered geometry on this tick
PVector normalAverage = new PVector();
PVector reflectionTotal = new PVector();
PVector collisionPos = null;
List<Entity> collidedEntities = new ArrayList<Entity>();
for (Geometry geo : entities.getByClass(Geometry.class)) {
if (!geo.solid || !isRectInsideRect(this.boundingBox, geo.boundingBox)) continue;
for (PVector[] line : geo.getLines()) {
// this is basically a circle-shaped trace:
float dist = getLineSegmentLineSegmentDist(line, new PVector[] {this.origin, this.lastTickOrigin});
if (dist < this.radius) {
PVector normal = PVector.sub(line[1], line[0]).rotate(-HALF_PI).normalize(); // normalized vector perpendicular to the line (on the left side -> outside)
normalAverage.add(normal);
float normalDot = PVector.dot(velocityNormalized, normal); // -1.0 == head on collision; 0.0 == parallel to the line
if (normalDot > 0.0) continue; // ignore inside collisions (actually only allows collisions from the left side of a line, which is our "normal")
geometryIntersection.add(normal.copy().setMag(this.radius - dist)); // save how far the circle intersects this line
PVector reflectionNormalized = PVector.sub(velocityNormalized, PVector.mult(normal, 2).mult(normalDot)); // https://math.stackexchange.com/a/13263
reflectionTotal.add(reflectionNormalized);
if (collisionCount == 0) { // only do for the first line that was hit
collisionPos = getClosestPointOnLineSegment(line, this.origin);
}
collidedEntities.add(geo);
collisionCount++;
}
}
}
if (collisionCount < 1) return;
normalAverage.normalize();
float collisionForce = abs(PVector.dot(velocityNormalized, normalAverage) * velocity.mag());
// we decide whether the collision is a roll/bounce (or in-between)
// depending on the amount of force the line was hit by
float reflectionMult = lerp(this.roll, this.bounce, constrain(collisionForce * 0.5, 0.0, 1.0));
this.origin.add(geometryIntersection); // move circle out of geometry
this.velocity = PVector.mult(reflectionTotal.setMag(this.velocity.mag()), reflectionMult); // update velocity
this.onGround = this.origin.y < collisionPos.y;
this.lastCollision = new Collision(
levels.getCurrent().currentTick,
collisionPos,
normalAverage,
collisionForce,
collidedEntities.toArray(new Entity[0])
);
this.OnCollision(this.lastCollision);
}
void updateBoundingBox() {
this.boundingBox = this.getBoundingBoxForRadius(this.radius * 2);
}
void OnTick() {
this.cleanupInVoid();
this.velocity.y += this.mass * GRAVITY;
this.origin.add(this.velocity);
this.updateCollision();
if (this.lastCollision != null && this.lastCollision.tick < levels.getCurrent().currentTick - 5)
this.onGround = false;
this.updateBoundingBox();
this.lastTickOrigin = this.origin.copy();
}
void OnCollision(Collision collision) {}
}