Files
football/docs/plans/2026-05-08-m1-engine.md
2026-05-12 10:54:09 +03:00

187 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# План: M1 - Core Engine & Basic Rendering
**Цель:** Создать базовое окно игры, отрисовать хоккейную площадку и одну статичную шайбу, настроить основной игровой цикл.
**Архитектура:**
- Использование Ebitengine для рендеринга.
- Разделение на `World` (состояние) и `Renderer` (отрисовка).
- Точка входа в `cmd/game/main.go`.
**Стек:** Go, Ebitengine.
**Спека:** [docs/specs/2026-05-08-hockey-design.md](../specs/2026-05-08-hockey-design.md)
---
## Файловая структура
- `cmd/game/main.go` — точка входа, инициализация Ebitengine.
- `internal/game/world.go` — структура World, хранящая состояние игры.
- `internal/render/renderer.go` — логика отрисовки объектов на экране.
- `internal/entities/puck.go` — определение структуры Puck.
## Задачи
### Задача 1: Инициализация проекта и окна
**Файлы:**
- Создать: `cmd/game/main.go`
- [ ] **Шаг 1: Минимальный код для запуска окна**
```go
package main
import (
"log"
"github.com/hajimehoshi/ebiten/v2"
)
type Game struct{}
func (g *Game) Update() error { return nil }
func (g *Game) Draw(screen *ebiten.Image) { screen.Fill(ebiten.ColorWhite) }
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return 800, 600 }
func main() {
ebiten.SetWindowSize(800, 600)
ebiten.SetWindowTitle("Hockey Sim")
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}
```
- [ ] **Шаг 2: Запуск и проверка**
`go run cmd/game/main.go`
Ожидание: Открывается белое окно 800x600.
- [ ] **Шаг 3: Коммит**
`git add cmd/game/main.go`
`git commit -m "feat(engine): initialize ebitengine window"`
### Задача 2: Определение сущности Шайбы
**Файлы:**
- Создать: `internal/entities/puck.go`
- [ ] **Шаг 1: Структура Puck**
```go
package entities
type Puck struct {
X, Y float64
}
func NewPuck(x, y float64) *Puck {
return &Puck{X: x, Y: y}
}
```
- [ ] **Шаг 2: Коммит**
`git add internal/entities/puck.go`
`git commit -m "feat(entities): add puck entity"`
### Задача 3: Создание игрового мира
**Файлы:**
- Создать: `internal/game/world.go`
- [ ] **Шаг 1: Структура World**
```go
package game
import "football/internal/entities"
type World struct {
Puck *entities.Puck
}
func NewWorld() *World {
return &World{
Puck: entities.NewPuck(400, 300),
}
}
```
- [ ] **Шаг 2: Коммит**
`git add internal/game/world.go`
`git commit -m "feat(game): add world state"`
### Задача 4: Реализация базового рендерера
**Файлы:**
- Создать: `internal/render/renderer.go`
- [ ] **Шаг 1: Функция отрисовки мира**
```go
package render
import (
"football/internal/game"
"github.com/hajimehoshi/ebiten/v2"
"image/color"
)
type Renderer struct{}
func (r *Renderer) DrawWorld(screen *ebiten.Image, world *game.World) {
// Отрисовка льда (белый фон)
screen.Fill(color.RGBA{240, 240, 240, 255})
// Отрисовка шайбы (черный квадрат для начала)
// В Ebitengine для простых фигур используем vector или маленькие изображения.
// Для M1 достаточно закрасить область.
}
```
*Примечание: Для полноценного круга потребуется vector package, но для M1 начнем с простого заполнения.*
- [ ] **Шаг 2: Коммит**
`git add internal/render/renderer.go`
`git commit -m "feat(render): add basic world renderer"`
### Задача 5: Интеграция в Game Loop
**Файлы:**
- Изменить: `cmd/game/main.go`
- [ ] **Шаг 1: Подключение World и Renderer**
```go
package main
import (
"football/internal/game"
"football/internal/render"
"github.com/hajimehoshi/ebiten/v2"
"log"
)
type Game struct {
world *game.World
renderer *render.Renderer
}
func (g *Game) Update() error { return nil }
func (g *Game) Draw(screen *ebiten.Image) {
g.renderer.DrawWorld(screen, g.world)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return 800, 600 }
func main() {
ebiten.SetWindowSize(800, 600)
ebiten.SetWindowTitle("Hockey Sim")
gameInstance := &Game{
world: game.NewWorld(),
renderer: &render.Renderer{},
}
if err := ebiten.RunGame(gameInstance); err != nil {
log.Fatal(err)
}
}
```
- [ ] **Шаг 2: Запуск и проверка**
`go run cmd/game/main.go`
Ожидание: Окно с серым фоном.
- [ ] **Шаг 3: Коммит**
`git add cmd/game/main.go`
`git commit -m "feat(engine): integrate world and renderer into loop"`