# План: Физика и динамика шайбы (M2) **Цель:** Реализовать трение, потерю энергии при отскоках и порог остановки для шайбы. **Архитектура:** Внедрение коэффициентов трения (`Friction`) и упругости (`Restitution`) в логику обновления мира. Использование простого множительного затухания скорости. **Стек:** Go, Ebitengine (для контекста мира). **Спека:** [docs/specs/2026-05-08-physics-dynamics-design.md](../specs/2026-05-08-physics-dynamics-design.md) --- ## Файловая структура - `internal/game/world.go` — добавление констант физики и обновление логики `Update`. - `internal/game/world_test.go` — новые unit-тесты для проверки затухания скорости и потерь при отскоках. --- ## Задачи ### Задача 1: Константы физики **Файлы:** - Изменить: `internal/game/world.go` - [ ] **Шаг 1: Добавить константы в начало файла** ```go const ( PuckFriction = 0.99 // Коэффициент трения (каждый кадр) PuckRestitution = 0.8 // Коэффициент упругости (при ударе о борт) PuckStopThreshold = 0.1 // Порог остановки ) ``` - [ ] **Шаг 2: Коммит** ```bash git add internal/game/world.go git commit -m "phys: add physics constants" ``` ### Задача 2: Реализация трения и порога остановки **Файлы:** - Изменить: `internal/game/world.go` - [ ] **Шаг 1: Обновить `World.Update` для применения трения** В начале `Update` добавить: ```go w.puck.Velocity.X *= PuckFriction w.puck.Velocity.Y *= PuckFriction if math.Abs(w.puck.Velocity.X) < PuckStopThreshold { w.puck.Velocity.X = 0 } if math.Abs(w.puck.Velocity.Y) < PuckStopThreshold { w.puck.Velocity.Y = 0 } ``` - [ ] **Шаг 2: Коммит** ```bash git add internal/game/world.go git commit -m "phys: implement puck friction and stop threshold" ``` ### Задача 3: Реализация потерь при отскоках **Файлы:** - Изменить: `internal/game/world.go` - [ ] **Шаг 1: Обновить логику отскоков в `World.Update`** Заменить инверсию скорости на инверсию с множителем `PuckRestitution`. Пример для X: ```go if w.puck.X - w.puck.Radius <= 0 || w.puck.X + w.puck.Radius >= float64(screenWidth) { w.puck.Velocity.X = -w.puck.Velocity.X * PuckRestitution // Добавить коррекцию позиции, чтобы не застрять в стене if w.puck.X - w.puck.Radius < 0 { w.puck.X = w.puck.Radius } else if w.puck.X + w.puck.Radius > float64(screenWidth) { w.puck.X = float64(screenWidth) - w.puck.Radius } } ``` Аналогично для Y. - [ ] **Шаг 2: Коммит** ```bash git add internal/game/world.go git commit -m "phys: implement energy loss on bounce" ``` ### Задача 4: Тестирование физики **Файлы:** - Создать: `internal/game/world_test.go` - [ ] **Шаг 1: Написать тест на затухание скорости** ```go func TestPuckFriction(t *testing.T) { w := NewWorld() w.puck.Velocity = Vector2{X: 10, Y: 0} w.Update() if w.puck.Velocity.X >= 10 { t.Errorf("Expected velocity to decrease due to friction, got %f", w.puck.Velocity.X) } } ``` - [ ] **Шаг 2: Написать тест на отскок с потерей энергии** ```go func TestPuckBounceLoss(t *testing.T) { w := NewWorld() w.puck.X = 1 // Почти у левого края w.puck.Velocity = Vector2{X: -10, Y: 0} w.Update() expected := 10 * PuckRestitution if math.Abs(w.puck.Velocity.X - expected) > 0.001 { t.Errorf("Expected velocity after bounce to be %f, got %f", expected, w.puck.Velocity.X) } } ``` - [ ] **Шаг 3: Запустить тесты** `go test ./internal/game/ -v` - [ ] **Шаг 4: Коммит** ```bash git add internal/game/world_test.go git commit -m "test: add physics tests" ```