Как улучшить мою симуляцию трехмерного пространства в Pygame? - PullRequest
2 голосов
/ 02 ноября 2019

Что касается моего предыдущего вопроса, я пытаюсь смоделировать трехмерное пространство в Pygame. До сих пор я придумал очень простую идею, которая использует третью координату в качестве знаменателя для «сжатия» (я уверен, что здесь есть некоторая терминология, о которой я не знаю) самых дальних точек вокруг центра экрана и уменьшения их размеров. .

Может ли кто-нибудь предложить простое улучшение этой идеи? Я чувствую, что могу просто настроить этот знаменатель, используемый для проекции (см. Код), чтобы создать более точную симуляцию.

Если вы запустите код ниже, у вас будет неплохая симуляция(скажем) космический корабль, проходящий мимо некоторых звезд (нажимая w или s). Они исчезают, если они далеко, и после этого создается новый. Но если я применяю вращение (a или d), становится очевидным, что симуляция идет не очень хорошо, поскольку я не проецирую 3D-точки на 2-й экран.

import pygame
import random
import numpy as np

pygame.init()
run=True

#screensize
screensize = (width,height)=(600,600)
center=(int(width/2),int(height/2))
screen = pygame.display.set_mode(screensize)

#delta mov
ds=0.1
do=0.0001

#Stars
points=[]
for i in range(1000):
    n1 = random.randrange(-5000,5000)
    n2 = random.randrange(-5000,5000)
    n3 = random.randrange(-30,30)
    points.append([n1,n2,n3])

while run:
    pygame.time.delay(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run=False

    ################## keys
    keys=pygame.key.get_pressed()

    if keys[pygame.K_w]:
        for p in points:
            p[2]-=ds
    if keys[pygame.K_s]:
        for p in points:
            p[2]+=ds

    if keys[pygame.K_a] or keys[pygame.K_d]:
        if keys[pygame.K_a]:
            for p in points:
                p[0]=np.cos(-do)*p[0]-np.sin(-do)*p[2]
                p[2]=np.sin(-do)*p[0]+np.cos(-do)*p[2]
        else:
            for p in points:
                p[0]=np.cos(do)*p[0]-np.sin(do)*p[2]
                p[2]=np.sin(do)*p[0]+np.cos(do)*p[2]


    ###############################projection###################

    for p in points:
        #this is to create new stars
        if p[2]<=-30 or p[2]>=30:
            p[0] = random.randrange(-5000,5000)
            p[1] = random.randrange(-5000,5000)
            p[2] =30
        else:
            #this is to ignore stars which are behind the ship
            if p[2]<=0:
                pass
            else:
                try:
                    #THIS IS THE PROJECTION I USE, I TAKE THE RADIUS BECAUSE I GUESS I'LL NEED IT... BUT I DON'T USE IT XD
                    r = ((p[0]**2+p[1]**2+p[2]**2)**(1/2))
                    pygame.draw.circle(screen,(255,255,0),(int(p[0]/p[2]+center[0]),int(p[1]/p[2]+center[1])),int(10/p[2]))
                #this is to prevent division by cero and alike
                except Exception as e:
                    pass

    pygame.display.update()
    screen.fill((0,0,0))


pygame.quit()

1 Ответ

3 голосов
/ 02 ноября 2019

В общих чертах достигается Однородные координаты . Ваш подход близок к этому.

Я рекомендую оперировать Декартовыми координатами , где 3 измерения имеют одинаковый масштаб.
Эмулировать Перспективная проекция , когда выНарисуйте точки.
Это означает, что вы должны вычислить w компонент Однородных координат в зависимости от глубины (координата z) точки (например, w = p[2] * 30 / 5000) и выполнить«перспективное деление» компонентов x, y и z на компонент * w, перед тем как начертить точки. Например:

#delta mov
ds=10
do=0.01

#Stars
points=[]
for i in range(1000):
    n1 = random.randrange(-5000,5000)
    n2 = random.randrange(-5000,5000)
    n3 = random.randrange(-5000,5000)
    points.append([n1,n2,n3])

while run:
    pygame.time.delay(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run=False

    ################## keys
    keys=pygame.key.get_pressed()

    if keys[pygame.K_w]:
        for p in points:
            p[2]-=ds
    if keys[pygame.K_s]:
        for p in points:
            p[2]+=ds

    if keys[pygame.K_a] or keys[pygame.K_d]:
        if keys[pygame.K_a]:
            for p in points:
                p[0], p[2] = np.cos(-do)*p[0]-np.sin(-do)*p[2], np.sin(-do)*p[0]+np.cos(-do)*p[2]
        else:
            for p in points:
                p[0], p[2] = np.cos(do)*p[0]-np.sin(do)*p[2], np.sin(do)*p[0]+np.cos(do)*p[2]

    ###############################projection###################

    screen.fill((0,0,0))
    for p in points:
        #this is to create new stars
        if p[2]<=-5000 or p[2]>=5000:
            p[0], p[1], p[2] = random.randrange(-5000,5000), random.randrange(-5000,5000), 5000
        else:
            #this is to ignore stars which are behind the ship
            if p[2]<=0:
                pass
            else:
                w = p[2] * 30 / 5000
                pygame.draw.circle(screen,(255,255,0),(int(p[0]/w+center[0]),int(p[1]/w+center[1])),int(10/w))

    pygame.display.update()

Более того, вращение не правильное. Когда вы делаете

p[0]=np.cos(-do)*p[0]-np.sin(-do)*p[2]
p[2]=np.sin(-do)*p[0]+np.cos(-do)*p[2]

, p[0] изменяется в первой строке, но исходное значение должно использоваться во 2-й строке.
Выполните присвоение "tuple" длярешить вопрос:

p[0], p[2] = np.cos(-do)*p[0]-np.sin(-do)*p[2], np.sin(-do)*p[0]+np.cos(-do)*p[2]
...