golang

Разработка игр с использованием Go и библиотеки Ebiten

  • суббота, 9 марта 2024 г. в 00:00:20
https://habr.com/ru/articles/798917/

Go является мощным и эффективным языком программирования, который можно использовать для создания игр. В этой статье мы рассмотрим разработку простой игры с использованием языка Go и библиотеки Ebiten, предназначенной для создания 2D игр.

Что такое Ebiten?

Ebiten - это простая и эффективная библиотека для создания 2D игр на языке Go. Она предоставляет удобные инструменты для рисования графики, обработки ввода и управления анимациями.

Установка и настройка

Перед тем как начать разработку игры, вам нужно установить библиотеку Ebiten. Вы можете сделать это с помощью следующей команды:

go get -u github.com/hajimehoshi/ebiten/v2

После установки библиотеки вы готовы начать создавать свою игру!

Пример игры "Платформер"

Давайте создадим игру «Платформер», где игрок управляет персонажем, преодолевая препятствия.

Шаг 1: Подключение необходимых пакетов

package main

import (
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
	"github.com/hajimehoshi/ebiten/v2/vector"
	"image/color"
)

Этот блок кода импортирует необходимые пакеты для работы с библиотекой Ebiten. В нем используются ebiten для создания игры, ebitenutil для загрузки изображений, inpututil для обработки ввода с клавиатуры и vector для рисования графики.

Шаг 2: Определение констант и переменных

const (
	screenWidth  = 2000    // Ширина игрового экрана
	screenHeight = 700     // Высота игрового экрана
)

var (
	playerImage     *ebiten.Image  // Изображение игрока
	backgroundImage *ebiten.Image  // Фоновое изображение
	playerX         = 0            // Координата X игрока на экране
	playerY         = 0            // Координата Y игрока на экране
	jumping         = false        // Флаг прыжка игрока
	jumpVelocity    = 0            // Скорость прыжка игрока
	gravity         = 3            // Сила гравитации
	groundLevel     = 630          // Уровень земли, на котором находится игрок

	obstacleX      = 250  // Координата X первого препятствия
	obstacleY      = 350  // Координата Y первого препятствия
	obstacleWidth  = 40   // Ширина первого препятствия
	obstacleHeight = 40   // Высота первого препятствия

	obstacle2X      = 460  // Координата X второго препятствия
	obstacle2Y      = 350  // Координата Y второго препятствия
	obstacle2Width  = 40   // Ширина второго препятствия
	obstacle2Height = 40   // Высота второго препятствия
)

Эти константы и переменные определяют параметры игрового процесса, такие как размеры экрана, изображения игрока и фона, координаты и размеры препятствий, а также параметры прыжка игрока (скорость и гравитация). Они неизменны в течение выполнения программы и используются для управления игровым процессом и отображением игровых объектов.

Шаг 3: Определение структуры игры и методов ее обновления и отрисовки

type Game struct{}

func (g *Game) Update() error {
	// Обновление игрового состояния
}

func (g *Game) Draw(screen *ebiten.Image) {
	// Отрисовка игровых объектов
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return outsideWidth, outsideHeight
}

В этом блоке кода определяется структура Game, которая представляет игру. Метод Update используется для обновления игрового состояния, Draw - для отрисовки игровых объектов, а Layout - для установки размеров экрана.

Шаг 4: Обновление игрового состояния

func (g *Game) Update() error
{
	if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
		return nil // Закрываем игру, возвращая nil
	}

	// Управление игроком
	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
		playerX -= 10 // Смещаем игрока влево при нажатии клавиши влево
	}
	if ebiten.IsKeyPressed(ebiten.KeyRight) {
		playerX += 10 // Смещаем игрока вправо при нажатии клавиши вправо
	}
	if ebiten.IsKeyPressed(ebiten.KeyUp) {
		if !jumping { // Если игрок не в состоянии прыжка
			jumping = true     // Устанавливаем флаг прыжка
			jumpVelocity = 50  // Устанавливаем скорость прыжка
		}
	}

	// Прыжок игрока
	if jumping {
		playerY -= jumpVelocity   // Обновляем положение игрока по вертикали
		jumpVelocity -= gravity   // Уменьшаем скорость прыжка из-за гравитации
		if playerY > groundLevel { // Если игрок достиг земли
			playerY = groundLevel  // Устанавливаем его на уровень земли
			jumping = false        // Сбрасываем флаг прыжка
		}
	} else if playerY < groundLevel {
		// Если игрок находится в воздухе выше уровня земли
		playerY = groundLevel // Устанавливаем его на уровень земли
	}

	// Ограничение перемещения игрока в пределах экрана
	if playerX < 0 {
		playerX = 0 // Не даем игроку выйти за левую границу экрана
	}
	if playerX > screenWidth-16 {
		playerX = screenWidth - 16 // Не даем игроку выйти за правую границу экрана
	}
	if playerY < 0 {
		playerY = 0 // Не даем игроку выйти за верхнюю границу экрана
	}
	if playerY > screenHeight-16 {
		playerY = screenHeight - 16 // Не даем игроку выйти за нижнюю границу экрана
	}

	// Проверка коллизий с препятствиями
	if checkCollision(playerX, playerY, 16, 16, obstacleX, obstacleY, obstacleWidth, obstacleHeight) {
		// Если произошла коллизия с первым препятствием
		playerX = obstacleX - 16 // Устанавливаем игрока перед препятствием
		playerY = obstacleY - 16 // Устанавливаем игрока на верхний край препятствия
	}

	if checkCollision(playerX, playerY, 16, 16, obstacle2X, obstacle2Y, obstacle2Width, obstacle2Height) {
		// Если произошла коллизия со вторым препятствием
		playerX = obstacle2X - 16 // Устанавливаем игрока перед вторым препятствием
		playerY = obstacle2Y - 16 // Устанавливаем игрока на верхний край второго препятствия
	}

	return nil
}
//Вспомогательная функция для проверки коллизий
func checkCollision(x1, y1, w1, h1, x2, y2, w2, h2 int) bool {
	return x1 < x2+w2 && x1+w1 > x2 && y1 < y2+h2 && y1+h1 > y2
}

Эта функция обновляет состояние игры каждый кадр, обрабатывает пользовательский ввод и обновляет положение игрока на основе нажатых клавиш. Она также обрабатывает прыжки игрока, гравитацию и проверку коллизий с препятствиями, чтобы предотвратить перемещение игрока через препятствия. Функция checkCollision проверяет, пересекаются ли два прямоугольника (игрок и препятствие) в пространстве.

Шаг 5: Отрисовка игровых объектов

func (g *Game) Draw(screen *ebiten.Image) {
	// Отображение фонового изображения с уменьшением размера в два раза
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Scale(0.8, 0.7) // Уменьшение размера в два раза
	screen.DrawImage(backgroundImage, op)

	// Рисование игрока с уменьшением размера в два раза
	geoM := ebiten.GeoM{}
	geoM.Translate(float64(playerX), float64(playerY))
	geoM.Scale(0.4, 0.4) // Уменьшение размера в два раза
	screen.DrawImage(playerImage, &ebiten.DrawImageOptions{GeoM: geoM})

	// Рисование препятствия 1
	vector.DrawFilledRect(screen, float32(obstacleX), float32(obstacleY), float32(obstacleWidth), float32(obstacleHeight), color.Gray{}, false)

	// Рисование препятствия 2
	vector.DrawFilledRect(screen, float32(obstacle2X), float32(obstacle2Y), float32(obstacle2Width), float32(obstacle2Height), color.Gray{}, false)

}

Эта функция отображает фоновое изображение, изображение игрока и два препятствия на экране. Она использует методы из библиотеки Ebiten для отрисовки изображений и векторных прямоугольников на экране игры.

Шаг 6: Запуск игры

func main() {
	// Загрузка изображения игрока
	img, _, err := ebitenutil.NewImageFromFile("person.png")
	if err != nil {
		panic(err)
	}
	playerImage = img

	// Загрузка фонового изображения
	backgroundImage, _, err = ebitenutil.NewImageFromFile("Game_Background.png")
	if err != nil {
		panic(err)
	}

	playerX = 50 // Персонаж начинает игру немного правее

	// Запуск игры
	game := &Game{}
	if err := ebiten.RunGame(game); err != nil {
		panic(err)
	}
}
Особенности игры:
Особенности игры:
  • Управление игроком: Игрок может управлять персонажем с помощью клавиш клавиатуры, перемещая его влево, вправо и выполняя прыжки.

  • Гравитация и прыжок: Есть гравитация, которая воздействует на персонажа, а также возможность выполнять прыжки.

  • Ограничение перемещения: Игрок не может выйти за пределы экрана.

  • Коллизии с препятствиями: Есть несколько препятствий на уровне, и персонаж не может пройти сквозь них.

Вывод

В результате создания этой игры вы получили работающий прототип с простой механикой и управлением. Даже на основе этого простого примера можно увидеть потенциал Ebiten для создания более сложных игр.

Вы можете найти полный исходный код этого проекта на GitHub