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
|
// main.go
package main
import (
"math"
"math/rand/v2"
"syscall/js"
"time"
)
type Ball struct {
X, Y float64
VX, VY float64
Radius float64
Color string
Mass float64
}
var (
canvas js.Value
ctx js.Value
balls []Ball
gravity float64 = 0.5 // 重力加速度
ground float64 = 300 // 地面の位置
width float64 = 500 // キャンバスの幅
height float64 = 400 // キャンバスの高さ
)
// 物理演算の更新処理
func updatePhysics() {
// 重力と地面の衝突を適用
for i := range balls {
// 重力の影響を適用
balls[i].VY += gravity
// 位置の更新
balls[i].X += balls[i].VX
balls[i].Y += balls[i].VY
// 水平方向の壁との衝突
if balls[i].X < balls[i].Radius {
balls[i].X = balls[i].Radius
balls[i].VX = -balls[i].VX * 0.9 // 水平方向の反発係数
} else if balls[i].X > width-balls[i].Radius {
balls[i].X = width - balls[i].Radius
balls[i].VX = -balls[i].VX * 0.9
}
// 地面に衝突した場合
if balls[i].Y > ground-balls[i].Radius {
balls[i].Y = ground - balls[i].Radius
balls[i].VY = -balls[i].VY * 0.7 // 反発係数
// 水平方向の速度に摩擦を適用
balls[i].VX *= 0.95
}
}
// ボール同士の衝突判定と応答
for i := 0; i < len(balls); i++ {
for j := i + 1; j < len(balls); j++ {
handleCollision(&balls[i], &balls[j])
}
}
}
// ボール同士の衝突を処理する関数
func handleCollision(a, b *Ball) {
// 2つのボール間の距離を計算
dx := b.X - a.X
dy := b.Y - a.Y
distance := math.Sqrt(dx*dx + dy*dy)
// 衝突判定: 2つのボールの半径の和より距離が小さい場合
minDistance := a.Radius + b.Radius
if distance < minDistance {
// 衝突している場合、ボールを押し戻して重ならないようにする
overlap := minDistance - distance
// 押し戻す方向を計算(正規化)
if distance > 0 {
dx /= distance
dy /= distance
} else {
// 完全に重なっている場合のランダムな方向
angle := rand.Float64() * 2 * math.Pi
dx = math.Cos(angle)
dy = math.Sin(angle)
distance = 0.1 // 非ゼロの小さな値
}
// 質量に基づいて押し戻し量を計算
totalMass := a.Mass + b.Mass
aRatio := b.Mass / totalMass
bRatio := a.Mass / totalMass
// 位置の修正
a.X -= dx * overlap * aRatio
a.Y -= dy * overlap * aRatio
b.X += dx * overlap * bRatio
b.Y += dy * overlap * bRatio
// 衝突応答: 運動量保存則に基づく速度の計算
// 衝突軸に沿った速度成分
vax := a.VX*dx + a.VY*dy
vay := a.VX*-dy + a.VY*dx
vbx := b.VX*dx + b.VY*dy
vby := b.VX*-dy + b.VY*dx
// 一次元の弾性衝突
// 衝突軸に沿った新しい速度(反発係数0.9)
e := 0.9 // 反発係数
newVax := (vax*(a.Mass-b.Mass) + 2*b.Mass*vbx) / totalMass * e
newVbx := (vbx*(b.Mass-a.Mass) + 2*a.Mass*vax) / totalMass * e
// 新しい速度に更新(衝突軸に直交する成分はそのまま)
a.VX = newVax*dx - vay*dy
a.VY = newVax*dy + vay*dx
b.VX = newVbx*dx - vby*dy
b.VY = newVbx*dy + vby*dx
}
}
// 描画処理
func draw() {
// キャンバスをクリア
ctx.Call("clearRect", 0, 0, width, height)
// 地面を描画
ctx.Set("fillStyle", "#333")
ctx.Call("fillRect", 0, ground, width, height-ground)
// クリアボタンを描画
ctx.Set("fillStyle", "#f44336")
ctx.Call("fillRect", width-100, ground+10, 90, 30)
ctx.Set("fillStyle", "white")
ctx.Set("font", "14px Arial")
ctx.Set("textAlign", "center")
ctx.Set("textBaseline", "middle")
ctx.Call("fillText", "ボールを削除", width-55, ground+25)
// 各ボールを描画
for _, ball := range balls {
ctx.Call("beginPath")
ctx.Call("arc", ball.X, ball.Y, ball.Radius, 0, 2*math.Pi)
ctx.Set("fillStyle", ball.Color)
ctx.Call("fill")
ctx.Set("strokeStyle", "rgba(0,0,0,0.3)")
ctx.Set("lineWidth", 2)
ctx.Call("stroke")
}
}
// requestAnimationFrame のコールバック関数
func render(this js.Value, args []js.Value) interface{} {
updatePhysics()
draw()
js.Global().Call("requestAnimationFrame", js.FuncOf(render))
return nil
}
// マウスクリックイベントのハンドラ
func handleClick(this js.Value, args []js.Value) interface{} {
event := args[0]
rect := canvas.Call("getBoundingClientRect")
x := event.Get("clientX").Float() - rect.Get("left").Float()
y := event.Get("clientY").Float() - rect.Get("top").Float()
// クリアボタンがクリックされたかチェック
if x >= width-80 && x <= width-10 && y >= ground+10 && y <= ground+40 {
// ボールをすべて削除
balls = []Ball{}
return nil
}
// 地面より下をクリックした場合は何もしない
if y > ground {
return nil
}
// 新しいボールを追加
addBall(x, y)
return nil
}
// 指定位置にボールを追加
func addBall(x, y float64) {
r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 3))
radius := 10 + r.Float64()*15 // 10~25pxのランダムな半径
// 質量は半径の2乗に比例すると仮定
mass := radius * radius
balls = append(balls, Ball{
X: x,
Y: y,
VX: (r.Float64()*10 - 5), // -5~5のランダムな初速度
VY: (r.Float64()*5 - 10), // -10~-5のランダムな初速度(上向き)
Radius: radius,
Color: randomColor(r),
Mass: mass,
})
}
func main() {
r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 3))
// HTML の canvas 要素を取得
document := js.Global().Get("document")
canvas = document.Call("getElementById", "myCanvas")
if canvas.IsUndefined() {
js.Global().Call("alert", "Canvas要素が見つかりません!")
return
}
// キャンバスのサイズを設定
width = canvas.Get("width").Float()
height = canvas.Get("height").Float()
ground = height * 0.75 // 地面の位置をキャンバスの75%の位置に
ctx = canvas.Call("getContext", "2d")
// マウスクリックイベントの登録
canvas.Call("addEventListener", "click", js.FuncOf(handleClick))
// ボールを複数追加
for i := 0; i < 5; i++ {
xPos := r.Float64()*(width-80) + 40 // 余裕を持たせる
yPos := r.Float64()*(ground/2) + 20
radius := 10 + r.Float64()*15 // 10~25pxのランダムな半径
// 質量は半径の2乗に比例すると仮定
mass := radius * radius
balls = append(balls, Ball{
X: xPos,
Y: yPos,
VX: (r.Float64()*10 - 5), // -5~5のランダムな初速度
VY: 0, // 初速度ゼロ
Radius: radius,
Color: randomColor(r),
Mass: mass,
})
}
// アニメーション開始
js.Global().Call("requestAnimationFrame", js.FuncOf(render))
// 永久ループ
select {}
}
// ランダムな色を生成する関数
func randomColor(r *rand.Rand) string {
colors := []string{"#e74c3c", "#3498db", "#2ecc71", "#9b59b6", "#f1c40f", "#1abc9c", "#e67e22", "#34495e"}
return colors[r.IntN(len(colors))]
}
|