5.0 KiB
План: M1 - Core Engine & Basic Rendering
Цель: Создать базовое окно игры, отрисовать хоккейную площадку и одну статичную шайбу, настроить основной игровой цикл.
Архитектура:
- Использование Ebitengine для рендеринга.
- Разделение на
World(состояние) иRenderer(отрисовка). - Точка входа в
cmd/game/main.go.
Стек: Go, Ebitengine. Спека: docs/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: Минимальный код для запуска окна
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.gogit commit -m "feat(engine): initialize ebitengine window"
Задача 2: Определение сущности Шайбы
Файлы:
-
Создать:
internal/entities/puck.go -
Шаг 1: Структура Puck
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.gogit commit -m "feat(entities): add puck entity"
Задача 3: Создание игрового мира
Файлы:
-
Создать:
internal/game/world.go -
Шаг 1: Структура World
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.gogit commit -m "feat(game): add world state"
Задача 4: Реализация базового рендерера
Файлы:
-
Создать:
internal/render/renderer.go -
Шаг 1: Функция отрисовки мира
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.gogit commit -m "feat(render): add basic world renderer"
Задача 5: Интеграция в Game Loop
Файлы:
-
Изменить:
cmd/game/main.go -
Шаг 1: Подключение World и Renderer
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.gogit commit -m "feat(engine): integrate world and renderer into loop"