为什么我的宇宙飞船不指向地球?

Why won't my Spaceship point toward the planet?

Small gif showing problem but I don't have 10 reputation yet (this is only my 2nd question) and thus have to use link

我有一个简单的测试程序,其中有一艘宇宙飞船,它应该指向一个行星,但它却指向奇怪的方向。

该程序的目的是测试我的测试程序是否可以获得从宇宙飞船到地球的角度,所以替代方法不起作用,因为我需要角度来确定施加“重力”的方向" 在(这就是这个测试的目的)。

这也是脚本重复多次但只以图形方式显示最终角度的原因。

我的程序使用从 SO 问题中获得的代码

“Chris St Pierre”的回答只是给我一个试图将浮点数除以零 (?) 的错误。

“ali_m”的回答正好给了我一个这样的问题。

我正在使用“Colin Basnett”的答案,这对我也不起作用,但这是迄今为止我最喜欢的方法,因为它不需要插件(而且因为它很短而且不仅直接向我抛出一个错误)。

我把它改成了下面的函数:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def get_angle_between(x0,y0,x1,y1): 
    v1 = Vector(x0, y0)
    v2 = Vector(x1, y1)
    
    v1_theta = math.atan2(v1.y, v1.x)
    v2_theta = math.atan2(v2.y, v2.x)
    
    r = (v2_theta - v1_theta) * (180.0 / math.pi)
    
    return r

在宇宙飞船精灵的“移动”函数中由这个脚本调用:

if gravityJsonQ:
            for item in planets:
                centreOfGravityX = planets[item]["x"] + (planets[item]["s"] / 2)
                centreOfGravityY = planets[item]["y"] + (planets[item]["s"] / 2)
                centreOfGravityGravity = float(planets[item]["g"])
                pendingUtil = get_points(prevSubPositionX,prevSubPositionY,subPositionX,subPositionY)
                for item2 in pendingUtil:
                    cfx,cfy = item2
                    circular_percentage = get_angle_between(cfx,cfy,centreOfGravityX,centreOfGravityY) / 3.6

circular_percentage (cp) 本质上是 degrees / 3.6 (逆时针,虽然 link 是顺时针角度,但我尝试从 100cp (360deg) 中减去它无济于事,所以我怀疑是问题所在)

get_points() 是“Bresenham 线算法”,它工作正常。

planets 是以下字典:

{"Earthlike": {"x": 375, "y": 375, "s": 200, "i": "earthlike_1_beveled.png", "g": 11}}

我试着摆弄了一下,看看它是否会开始工作,但主要问题是我不了解所涉及的任何数学,所以 linked 维基百科文章(s ) 就在我头上。 我(坦率地说)不知道导致问题的原因或解决方法。

这里是一个 link 下载程序及其纹理的所有 194KB(解压缩后实际上小了 10KB)。 (使用WASD/arrow键移动,问题出在第49到63行或第100到108行(第一行是#1而不是#0)):

https://www.filehosting.org/file/details/920907/SOQ.zip

可能有一些不必要的代码,因为我刚刚得到我的主程序并删除了大部分不需要的位。

以防万一,这里是代码(它在 zip 中,但我想我可能应该把它放在这里,即使它在没有纹理的情况下无法运行(真实的词?)):

#See lines (this line is #1) - 49 to 63 - and - 100 to 108


import json, math, os, pygame, sys, time
from pygame.locals import *
pygame.init()
baseFolder = __file__[:-10]
FPS = 30
FramePerSec = pygame.time.Clock()
xVelocity = yVelocity = rVelocity = float(0)
def get_points(x0,y0,x1,y1):
    pointlist = []
    x0,y0 = int(x0),int(y0)
    x1,y1 = int(x1),int(y1)
    dx = abs(x1-x0)
    dy = abs(y1-y0)
    if x0 < x1: sx = 1
    else: sx = -1
    if y0 < y1: sy = 1
    else: sy = -1
    err = dx-dy
    while True:
        pointlist.append((x0,y0))
        if x0 == x1 and y0 == y1: return pointlist
        e2 = 2 * err
        if e2 > -dy:
            err = err - dy
            x0 += sx
        if e2 < dx:
            err = err + dx
            y0 += sy
screen_size = 750
spaceship_texture = "spaceship.png"
spaceship_texture = spaceship_texture.replace("\n","")
spaceship_size = 60
gravityJsonQ = True
planets = {"Earthlike": {"x": 375, "y": 375, "s": 200, "i": "earthlike_1_beveled.png", "g": 11}}
displaySurf = pygame.display.set_mode((screen_size,screen_size))
displaySurf.fill((0,0,0))
subPositionX = subPositionY = float(screen_size / 2)
circular_percentage = 0




#Problem:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def get_angle_between(x0,y0,x1,y1): 
    v1 = Vector(x0, y0)
    v2 = Vector(x1, y1)
    
    v1_theta = math.atan2(v1.y, v1.x)
    v2_theta = math.atan2(v2.y, v2.x)
    
    r = (v2_theta - v1_theta) * (180.0 / math.pi)
    
    return r

#or mabye...




class Spaceship(pygame.sprite.Sprite):
    def __init__(self):
        global baseFolder, screen_size, spaceship_images, spaceship_size, spaceship_texture
        super().__init__()
        spaceship_images = {}
        for pendingUtil in range(0,100): spaceship_images[str(pendingUtil)]  = pygame.image.load(baseFolder + "\" + spaceship_texture + ".texture_map\" + str(pendingUtil) + ".png")
        self.image = spaceship_images["0"]
        self.surf = pygame.Surface((int(spaceship_size), int(spaceship_size)))
        self.rect = self.surf.get_rect(center = (int(screen_size / 2),int(screen_size / 2)))
        self.image = pygame.transform.scale(self.image,(spaceship_size,spaceship_size))
    def move(self):
        global circular_percentage, rVelocity, prevSubPositionX, prevSubPositionY, subPositionX, subPositionY, xVelocity, yVelocity
        pressed_keys = pygame.key.get_pressed()
        if pressed_keys[K_UP] or pressed_keys[K_w]:
            yVelocity -= 0.1
        if pressed_keys[K_DOWN] or pressed_keys[K_s]:
            yVelocity += 0.1
        if pressed_keys[K_LEFT] or pressed_keys[K_a]:
            xVelocity -= 0.1
        if pressed_keys[K_RIGHT] or pressed_keys[K_d]:
            xVelocity += 0.1
        prevSubPositionX,prevSubPositionY = subPositionX,subPositionY
        subPositionX += xVelocity
        subPositionY += yVelocity
        
        
        
        
        #Problem:
        
        if gravityJsonQ:
            for item in planets:
                centreOfGravityX = planets[item]["x"] + (planets[item]["s"] / 2)
                centreOfGravityY = planets[item]["y"] + (planets[item]["s"] / 2)
                centreOfGravityGravity = float(planets[item]["g"])
                pendingUtil = get_points(prevSubPositionX,prevSubPositionY,subPositionX,subPositionY)
                for item2 in pendingUtil:
                    cfx,cfy = item2
                    circular_percentage = get_angle_between(cfx,cfy,centreOfGravityX,centreOfGravityY) / 3.6
        
        #Problem will (very likely) be either here or noted area above
        
        
        
        
        while circular_percentage < 0: circular_percentage += 100
        while circular_percentage > 99: circular_percentage -= 100
        self.rect = self.surf.get_rect(center = (int(subPositionX),int(subPositionY)))
        self.image = spaceship_images[str(int(circular_percentage))]
        self.image = pygame.transform.scale(self.image,(spaceship_size,spaceship_size))
Player = Spaceship()
all_sprites = pygame.sprite.Group()
all_sprites.add(Player)
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    displaySurf.fill((0,0,0))
    for item in planets:
        current_planet_image = pygame.image.load(planets[item]["i"])
        current_planet_image = pygame.transform.scale(current_planet_image,(planets[item]["s"],planets[item]["s"]))
        displaySurf.blit(current_planet_image,(planets[item]["x"],planets[item]["y"]))
    for entity in all_sprites:
        displaySurf.blit(entity.image,entity.rect)
        entity.move()
    pygame.display.update()
    FramePerSec.tick(FPS)

注意,Pygame 提供 pygame.math.Vector2 class. It is not necessary to paint 100 images of the space ship with different angles. You can rotate an image with pygame.transform.rotate. See How do I rotate an image around its center using PyGame?


点(x0,y0)到点(x1,y1)的向量为:

v = Vector(x1-x0, y1-y0)

矢量的角度为(参见):

math.atan2(y1-y0, x1-x0)

pygame坐标系左上角为(0, 0)。因此 y 轴指向下方。因此,您必须反转 y 轴才能计算角度。

get_angle_between函数:

def get_angle_between(x0, y0, x1, y1):
    v = Vector(x1-x0, y1-y0)
    return math.degrees(math.atan2(-v.y, v.x))

在上面的公式中,角度为0表示飞船指向右边。如果你的宇宙飞船以 0 角指向上方,你需要添加一个校正角(参见 ):

def get_angle_between(x0, y0, x1, y1):
    v = Vector(x1-x0, y1-y0)
    return math.degrees(math.atan2(-v.y, v.x)) - 90

在这个回答中我想解释一下计算的步骤。我想让它易于理解。当然,您可以跳过构建 Vector 对象并将所有内容放在一行代码中:

def get_angle_between(x0, y0, x1, y1):
    return math.degrees(math.atan2(y0-y1, x1-x0)) - 90

但是,计算的瓶颈是函数math.atan2