19.01.2022
статья
Простейшая 3d игра на питоне! (ray casting)

В этой статье я покажу как сделать простейшую 3d шаблонную игру на python.
Нам нужны несколько файлов:

  1. main.py
  2. settings.py
  3. map.py
  4. player.py
  5. ray_casting.py

Что такое pygame?

Это модуль, то есть какое-то дополнение к языку Python.С помощью pygame можно написать свою игру, иногда очень даже приличную.С помощью pygame написан шаблон для игры, о котором я вам сейчас рассказываю.Что-бы работать с pygame, нужно скачать его, то есть ввести в консоли: pip install pygame.


Файл main.py

Основной файл.

import pygame
from settings import *
from player import Player
import math
from map import world_map
from ray_casting import ray_casting

pygame.init()
sc = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
player = Player()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()
    player.movement()
    sc.fill(BLACK)

pygame.draw.rect(sc, BLUE, (0, 0, WIDTH, HALF_HEIGHT))
    pygame.draw.rect(sc, DARKGRAY, (0, HALF_HEIGHT, WIDTH, HALF_HEIGHT))

ray_casting(sc, player.pos, player.angle)
    pygame.display.flip()
    clock.tick(FPS)


Файл settings.py

В этом файле мы указываем некоторые настройки(переменные), которые пригодятся нам в разных файлах, например какой-то цвет.

import math

# game settings
WIDTH = 1500
HEIGHT = 800
HALF_WIDTH = WIDTH // 2
HALF_HEIGHT = HEIGHT // 2
FPS = 60
TILE = 100

# ray casting settings
FOV = math.pi / 3
HALF_FOV = FOV / 2
NUM_RAYS = 120
MAX_DEPTH = 800
DELTA_ANGLE = FOV / NUM_RAYS
DIST = NUM_RAYS / (2 * math.tan(HALF_FOV))
PROJ_COEFF = 3 * DIST * TILE
SCALE = WIDTH // NUM_RAYS

# player settings
player_pos = (HALF_WIDTH, HALF_HEIGHT)
player_angle = 0
player_speed = 2

# colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (220, 0, 0)
GREEN = (0, 220, 0)
BLUE = (0, 0, 255)
DARKGRAY = (40, 40, 40)
PURPLE = (120, 0, 120)


Файл map.py

В этом файле мы создаем карту в самом простом - текстовом формате.

from settings import *

text_map = [
    'WWWWWWWWWWWW',
    'W......W...W',
    'W..WWW...W.W',
    'W....W..WW.W',
    'W..W....W..W',
    'W..W...WWW.W',
    'W....W.....W',
    'WWWWWWWWWWWW'
]

world_map = set()
for j, row in enumerate(text_map):
    for i, char in enumerate(row):
        if char == 'W':
             world_map.add((i * TILE, j * TILE))


Файл player.py

В этом файле мы прописываем управление игроком.

from settings import *
import pygame
import math

class Player:
    def __init__(self):
       self.x, self.y = player_pos
       self.angle = player_angle

@property
    def pos(self):
        return (self.x, self.y)

def movement(self):
        sin_a = math.sin(self.angle)
        cos_a = math.cos(self.angle)
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:
             self.x = player_speed * cos_a
             self.y = player_speed * sin_a
        if keys[pygame.K_s]:
             self.x = -player_speed * cos_a
             self.y = -player_speed * sin_a
        if keys[pygame.K_a]:
             self.x = player_speed * sin_a
             self.y = -player_speed * cos_a
        if keys[pygame.K_d]:
             self.x = -player_speed * sin_a
             self.y = player_speed * cos_a
        if keys[pygame.K_LEFT]:
             self.angle -= 0.02
        if keys[pygame.K_RIGHT]:
             self.angle = 0.02


Файл ray_casting

Файл, где наша карта преобразовывается из 2d в 3d.

import pygame
from settings import *
from map import world_map

def ray_casting(sc, player_pos, player_angle):
    cur_angle = player_angle - HALF_FOV
    xo, yo = player_pos
    for ray in range(NUM_RAYS):
        sin_a = math.sin(cur_angle)
        cos_a = math.cos(cur_angle)
        for depth in range(MAX_DEPTH):
             x = xo depth * cos_a
             y = yo depth * sin_a
             if (x // TILE * TILE, y // TILE * TILE) in world_map:
                depth *= math.cos(player_angle - cur_angle)
                proj_height = min(PROJ_COEFF / (depth 0.0001), HEIGHT)
                c = 255 / (1 depth * depth * 0.0001)
                color = (c // 2, c, c // 3)
                pygame.draw.rect(sc, color, (ray * SCALE, HALF_HEIGHT - proj_height // 2, SCALE, proj_height))
                break
        cur_angle = DELTA_ANGLE


На этом все, сначала будет выдавать ошибку из-за того, что в таком формате сложно передать все пробелы, поэтому с ними надо будет чуть повозится.После "укрощения" пробелов можно запустить файл main.py и наслаждаться, пока что, не шедевром. Пишите в комментариях, если нужно продолжение.

Комментарии (2)
Ответ на этот комментарий
24 января, 2022 в 15:12
|
Кирилл Ш. Lanokip
круто!
Спасибо)