[Часть 1.5] Делаем свою 3D игру на Python

Данная статья находиться в разработке и может дополняться! Пишитесвое мнение в коментариях!

Делаем редактор!

Я хочу чтобы мой редактор карт бы похож на редактор игры «Doom II» так что написал я простой класс с редактором вот так:

import tkinter as tk
from tkinter import filedialog, simpledialog

class MapEditor:
    def __init__(self, root):
        self.root = root
        self.root.title("Map Editor")
        self.root.geometry("800x600")
        
        # Начальные настройки
        self.width = 10
        self.height = 8
        self.cell_size = 40
        self.zoom = 1.0
        
        self.init_map()
        self.create_widgets()
        self.bind_events()
        self.draw_map()

Здесь в этом коде мы :

  • Импортируем необходимые модули

  • Создаем класс редактора карт

  • Инициализируем основные параметры

  • Вызываем методы для создания интерфейса

Дальше для того чтобы мы могли взаимодействовать с програмой мы добавим в нее холст и панель инструментов:

    def create_widgets(self):
        # Холст для рисования карты
        self.canvas = tk.Canvas(
            self.root, 
            bg='#202020',
            cursor="crosshair"
        )
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # Панель инструментов
        self.toolbar = tk.Frame(self.root)
        self.toolbar.pack(fill=tk.X)
        
        # Кнопки управления
        buttons = [
            ('New Map', self.new_map, '#555'),
            ('Zoom +', lambda: self.change_zoom(0.1), '#777'),
            ('Zoom -', lambda: self.change_zoom(-0.1), '#777'),
            ('Save', self.save_map, '#388E3C'),
            ('Load', self.load_map, '#1976D2')
        ]
        
        for text, cmd, color in buttons:
            btn = tk.Button(
                self.toolbar,
                text=text,
                command=cmd,
                bg=color,
                fg='white',
                padx=8,
                pady=4
            )
            btn.pack(side=tk.LEFT, padx=2, pady=2)
Пояснение:
  • Создаем основной холст для отображения карты

  • Добавляем панель инструментов с кнопками

  • Настраиваем визуальный стиль элементов

Ну что давайте добавим в нашу карту схему создания самих карт! Напомню вот-так выглядит наша карта в файле map.txt:

8
WWWWWWWWWW
W        W
W WW  WW W
W WW     W
W        W
W     WW W
W        W
WWWWWWWWWW

И теперь для официального стиля я решил переименовать этот файл в ROOM1.map!

Спойлер

Как вы наверное поняли я назвал егоROOM1 потому что у нас будет система нескольких комнат и мы даже сделаем целое меню !

Вот и сам код :

    def init_map(self):
        """Инициализация новой карты с границами"""
        self.map_data = [[' ' for _ in range(self.width)] for _ in range(self.height)]
        
        # Создание границ
        for x in range(self.width):
            self.map_data[0][x] = 'W'
            self.map_data[-1][x] = 'W'
        for y in range(self.height):
            self.map_data[y][0] = 'W'
            self.map_data[y][-1] = 'W'

    def draw_map(self):
        """Отрисовка карты на холсте"""
        self.canvas.delete("all")
        cell = self.cell_size * self.zoom
        
        for y in range(self.height):
            for x in range(self.width):
                color = self.get_color(self.map_data[y][x])
                x0 = x * cell
                y0 = y * cell
                self.canvas.create_rectangle(
                    x0, y0, x0 + cell, y0 + cell,
                    fill=color,
                    outline='#333',
                    tags=("cell", f"{x},{y}")
                )

    def get_color(self, cell):
        """Возвращает цвет для типа ячейки"""
        return {
            'W': '#4A4A4A',  # Стена
            '.': '#6B6B6B',  # Пол
            ' ': '#202020'    # Пустота
        }.get(cell, '#202020')

Ну что-же давайте посмотрим на вышедший у нас редактор! Но сначала добавим еще чуть-чуть кода и магии обработки событий!

Скрытый текст
    def bind_events(self):
        """Привязка обработчиков событий"""
        # Мышь
        self.canvas.bind("", self.draw)
        self.canvas.bind("", self.draw)
        self.canvas.bind("", self.erase)
        self.canvas.bind("", self.erase)
        
        # Клавиатура
        self.root.bind("", lambda e: self.change_zoom(0.1))
        self.root.bind("", lambda e: self.change_zoom(-0.1))

    def draw(self, event):
        """Обработчик рисования ЛКМ"""
        x, y = self.get_cell_coords(event.x, event.y)
        if self.is_valid_cell(x, y):
            self.map_data[y][x] = 'W'
            self.draw_map()

    def erase(self, event):
        """Обработчик стирания ПКМ"""
        x, y = self.get_cell_coords(event.x, event.y)
        if self.is_valid_cell(x, y):
            self.map_data[y][x] = ' '
            self.draw_map()
    def change_zoom(self, delta):
        """Изменение масштаба"""
        self.zoom = max(0.5, min(2.0, self.zoom + delta))
        self.draw_map()

    def new_map(self):
        """Создание новой карты"""
        new_width = simpledialog.askinteger("New Map", "Width:", 
                                          minvalue=5, maxvalue=50)
        new_height = simpledialog.askinteger("New Map", "Height:", 
                                           minvalue=5, maxvalue=50)
        if new_width and new_height:
            self.width = new_width
            self.height = new_height
            self.init_map()
            self.draw_map()

    def save_map(self):
        """Сохранение карты в файл"""
        file_path = filedialog.asksaveasfilename(
            defaultextension=".map",
            filetypes=[("Map files", "*.map")]
        )
        if file_path:
            with open(file_path, 'w') as f:
                f.write(f"{self.height}\n")
                for row in self.map_data:
                    f.write(''.join(row).ljust(self.width, ' ') + '\n')

    def load_map(self):
        """Загрузка карты из файла"""
        file_path = filedialog.askopenfilename(
            filetypes=[("Map files", "*.map")]
        )
        if file_path:
            with open(file_path, 'r') as f:
                lines = [line.strip() for line in f.readlines()]
                self.height = int(lines[0])
                self.map_data = [list(line.ljust(self.width, ' ')[:self.width] 
                               for line in lines[1:self.height+1]]
                self.draw_map()

Итог!

В этой промежуточной статье мы сделали свой редактор уровней! Ну вот и его скриншоты:

Map editor.
Map editor.
Вот моя карта
Вот моя карта
Создание новой карты
613c2c18960befa0b4a2788b003bdcc6.gifДелаю карту :)
Делаю карту :)

Ну что-же спасибо и пока!!!

<- ПРОШЛАЯ СТАТЬЯ

Habrahabr.ru прочитано 19628 раз