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

问题描述

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 问题 Calculate angle (clockwise) between two points

获得的代码

“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

此脚本在 Spaceship 精灵的“移动”函数调用它:

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) 本质上是度数 / 3.6(逆时针,虽然链接是顺时针角度,但我尝试从 100cp(360 度)中减去它但无济于事,所以我怀疑这就是问题所在)

get_points() 是“Bresenham's line algorithm”,它运行良好。

行星是以下字典:

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

我试着摆弄了一下它是否会开始工作,但主要问题是我不懂任何涉及的数学,所以链接的维基百科文章直接在我脑海中浮现。 我(坦率地说)不知道导致问题的原因或解决方法

这是一个下载所有 194KB(解压缩时实际小 10KB)的程序及其纹理的链接。 (使用 WASD/箭头键移动,问题出在第 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,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,"g": 11}}
displaySurf = pygame.display.set_mode((screen_size,screen_size))
displaySurf.fill((0,0))
subPositionX = subPositionY = float(screen_size / 2)
circular_percentage = 0




#Problem:

class Vector:
    def __init__(self,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,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,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))
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))
    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 类。没有必要以不同的角度绘制 100 张太空船的图像。您可以使用 pygame.transform.rotate 旋转图像。见How do I rotate an image around its center using PyGame?


点(x0y0)到点(x1y1)的向量是:

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

向量的角度是(见How to know the angle between two points?):

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 角向上,则需要添加校正角度(请参阅 How to rotate an image(player) to the mouse direction?):

def get_angle_between(x0,v.x)) - 90

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

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

然而,计算中的瓶颈是函数math.atan2