-
Notifications
You must be signed in to change notification settings - Fork 0
/
physics.html
180 lines (146 loc) · 7.65 KB
/
physics.html
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
<!DOCTYPE html>
<html>
<head>
<meta name = viewport content = "width = device-width, initial-scale = 1">
<script async src = "//www.googletagmanager.com/gtag/js?id=G-C34XCNZ9G4"></script>
<script src = js/analytics.js></script>
<script src = js/highlight.min.js></script>
<script src = js/main.js></script>
<link href = assets/fontawesome/css/fontawesome.css rel = stylesheet>
<link href = assets/fontawesome/css/solid.css rel = stylesheet>
<link rel = stylesheet href = main.css>
<title>GreyHope · Demos · Ball Physics</title>
</head>
<body>
<section class = top>
<a href = index.html>GreyHope</a>
<i onclick = toggleBars(this) class = "fa-solid fa-bars"></i>
<i onclick = toggleTheme(this) class = theme></i>
<a href = index.html>Projects</a>
<a href = demos.html class = this>Demos</a>
<a href = tutorials.html>Tutorials</a>
</section>
<section class = main>
<h2>Ball Physics</h2>
<script type = editor>
<!DOCTYPE html>
<html>
<body>
<canvas id = canvas></canvas>
<script>
const ROWS = 10
const COLUMNS = 10
const RADIUS = 10
const GRAVITY = 0.2
const DAMPING = 0.99
class Ball {
constructor(x, y) {
this.x = x
this.y = y
this.speed = {x: 0.1, y: 0}
}
update() {
// make the ball fall
this.speed.y += GRAVITY
// slow it down slightly
this.x += this.speed.x *= DAMPING
this.y += this.speed.y *= DAMPING
// check collision with every other ball
balls.forEach(ball => {
// make sure it doesn't collide with itself
if (ball == this) return
// find distance and angle between both balls
const distance = Math.hypot(this.x - ball.x, this.y - ball.y)
const angle = Math.atan2(ball.y - this.y, ball.x - this.x)
// if the balls are too close, it means they have collided
if (distance < RADIUS * 2) {
// find how much they overlap
const x = Math.cos(angle) * (RADIUS * 2 - distance) / 2
const y = Math.sin(angle) * (RADIUS * 2 - distance) / 2
// make them move apart from each other
this.x -= x
this.y -= y
ball.x += x
ball.y += y
// alter their speed
this.speed.x -= x
this.speed.y -= y
ball.speed.x += x
ball.speed.y += y
}
})
// check collision with floor
if (this.y > canvas.height * 0.9) {
this.y = canvas.height * 0.9
this.speed.y = Math.abs(this.speed.y) * -1
}
// left wall
if (this.x < RADIUS) {
this.x = RADIUS
this.speed.x = Math.abs(this.speed.x)
}
// right wall
else if (this.x > canvas.width - RADIUS) {
this.x = canvas.width - RADIUS
this.speed.x = Math.abs(this.speed.x) * -1
}
// draw ball with arc
context.fillStyle = "#aaa"
context.beginPath()
context.arc(this.x, this.y, RADIUS, 0, 2 * Math.PI)
context.fill()
}
}
function resize() {
// set canvas dimensions
canvas.width = innerWidth
canvas.height = innerHeight
}
function loop() {
// clear screen
context.clearRect(0, 0, canvas.width, canvas.height)
// update all the balls
balls.forEach(ball => ball.update())
requestAnimationFrame(loop)
}
function start() {
// style the body and get rid of margins
document.body.style.margin = 0
document.body.style.overflow = "hidden"
document.body.style.background = "#555"
// when screen resizes, run the resize function
addEventListener("resize", resize)
resize()
// position all the balls in a grid
for (let y = 0; y < ROWS; y ++)
for (let x = 0; x < COLUMNS; x ++)
balls.push(new Ball(
canvas.width / 2 - ROWS * RADIUS + RADIUS * 2 * y,
canvas.height / 3 - COLUMNS * RADIUS * 2 + RADIUS * 2 * x
))
// activate game loop
loop()
}
const context = canvas.getContext("2d")
const balls = []
start()
</script>
</body>
</html>
</script>
<p>
In this demo, I have tried to make the code as minimal as possible.
The main collision calculations are done inside the <mark>update</mark> function.
</p>
<p>
Feel free to fiddle with the code and make your own combinations.
</p>
</section>
<footer>
<span>
© Copyright <span id = year></span> GreyHope ·
<a href = mailto:hello@greyhope.uk>hello@greyhope.uk</a>
</span>
</footer>
</body>
</html>