【问题标题】:Python - Make my spaceship shoot in the direction its facing Part 2Python - 让我的宇宙飞船朝它面对的方向射击第 2 部分
【发布时间】:2016-12-20 01:41:05
【问题描述】:

已编辑,问题底部的解决方案

在这个问题的一些好人的帮助下,我有了一些改进:Python - Shoot a bullet in the direction (angle in degrees) my spaceship is facing

现在的问题是,虽然使飞船朝所面对的方向加速的原理,但似乎不适用于弹丸。当子弹从船上以特定角度射出时,似乎有一个奇怪的偏移。

我将把代码放在下面,UPDATE() 和 BOOST() 方法负责使船移动并且可以正常工作。

射弹使用几乎相同的原理,没有加速。

这是一个视频,让您可以直观地看到正在运行的游戏并查看问题所在https://youtu.be/-s7LGuLhePI

这是我的 Ship 和 vector 类,它们与问题有关。 (我将省略并删除此处不需要显示的方法)

类 Ship 包含 ship 元素以及 Projectile 类

import pygame
import colors
import math
from vectors import Vector2D
from polygon import Polygon
from helpers import *

class Ship(Polygon) :

    def __init__(self, x, y, screen) :
        self.screen = screen
        self.pos = Vector2D(x, y)
        self.size = 18
        self.color = colors.green
        self.rotation = 0
        self.points = [
                        (-self.size, self.size),
                        (0, self.size / 3),
                        (self.size, self.size),
                        (0, -self.size)
                      ]
        self.translate((self.pos.x, self.pos.y))
        self.velocity = Vector2D(0, 0)
        self.projectiles = []

    def shoot(self) :
        p = Projectile(self.pos.x, self.pos.y, self.rotation, self.screen)
        self.projectiles.append(p)

    def turn(self, dir) :
        turn_rate = 4
        if dir == 'left' :
            deg = -turn_rate
        elif dir == 'right' :
            deg = turn_rate
        else :
            deg = 0

        self.rotate((self.pos.x, self.pos.y), deg)

        if self.rotation > 360 :
            self.rotation -= 360
        elif self.rotation < 0 :
            self.rotation += 360
        else :
            self.rotation += deg

        #print('HDG: ' + str(self.rotation))

    def boost(self) :
        #print(self.velocity.x, ',', self.velocity.y)
        force = Vector2D().create_from_angle(self.rotation, 0.1, True)

        #Limits the speed
        if (((self.velocity.x <= 4) and (self.velocity.x >= -4)) 
            or 
            ((self.velocity.y <= 4) and (self.velocity.y >= -4))) :
            self.velocity.add(force)

        #print('Velocity: ' + str(self.velocity.x) + ',' + str(self.velocity.y))

    def update(self) :
        #Adds friction
        f = 0.98
        self.velocity.mult((f, f))

        # Resets ship possition when it's out of the screen
        if self.pos.x > (self.screen.get_width() + self.size) :
            #print('COLLIDED RIGHT')
            self.pos.x -= self.screen.get_width() + self.size
            self.translate((-(self.screen.get_width() + self.size), 0))
        elif self.pos.x < -self.size :
            #print('COLLIDED LEFT')
            self.pos.x += self.screen.get_width() + self.size
            self.translate(((self.screen.get_width() + self.size), 0))
        if self.pos.y > (self.screen.get_height() + self.size) :
            #print('COLLIDED BOTTOM')
            self.pos.y -= self.screen.get_height() + self.size
            self.translate((0, -(self.screen.get_height() + self.size)))
        elif self.pos.y < -self.size :
            #print('COLLIDED TOP')
            self.pos.y += self.screen.get_height() + self.size
            self.translate((0, (self.screen.get_height() + self.size)))

        self.pos.x += self.velocity.x #TODO: simplify using V2D add function
        self.pos.y += self.velocity.y

        self.translate(self.velocity.tuple())

        #Update projectiles that have been shot
        for p in self.projectiles :
            p.update()

    def draw(self) :
        stroke = 3
        pygame.draw.polygon(self.screen, self.color, self.points, stroke)

        #Draws projectiles that have been shot
        for p in self.projectiles :
            p.draw()

class Projectile(object) :

    def __init__(self, x, y, ship_angle, screen) :
        self.screen = screen
        self.speed = 3 #Slow at the moment while we test it
        self.direction = ship_angle;
        self.pos = Vector2D(x, y)
        self.color = colors.green

    def update(self) :
        self.pos.add(Vector2D().create_from_angle(self.direction, self.speed, return_instance = True))

    def draw(self) :
        pygame.draw.circle(self.screen, self.color, self.pos.int().tuple(), 2, 0)

类向量(用于根据元素“航向”计算向量并应用速度)

import math

class Vector2D() :

    def __init__(self, x = None, y = None) :
        self.x = x
        self.y = y

    def create_from_angle(self, angle, magnitude, return_instance = False) :
        angle = math.radians(angle) - math.pi / 2
        x = math.cos(angle) * magnitude
        y = math.sin(angle) * magnitude
        self.x = x
        self.y = y

        if return_instance :
            return self

    def tuple(self) :
        return (self.x, self.y)

    def int(self) :
        self.x = int(self.x)
        self.y = int(self.y)
        return self

    def add(self, vector) :
        if isinstance(vector, self.__class__) : # vector is an instance of this class
            self.x += vector.x 
            self.y += vector.y
        else : # vector is a tuple
            self.x += vector[0]
            self.y += vector[1]

    def mult(self, vector) :
        if isinstance(vector, self.__class__) : # vector is an instance of this class
            self.x *= vector.x 
            self.y *= vector.y
        else : # vector is a tuple
            self.x *= vector[0]
            self.y *= vector[1]

基于标记答案的解决方案

Pygame.draw.circle() 只接受 INTEGER 值元组作为位置参数,由于我们正在进行的计算,这是不可能的,因为角度计算的结果总是浮点数。

解决方案是在 Projectile 类中将绘制方法更改为使用 Ellipse 而不是 Circle:

class Projectile(object) :

    def __init__(self, x, y, ship_angle, screen) :
        self.screen = screen
        self.speed = 3 #Slow at the moment while we test it
        self.direction = ship_angle;
        self.velocity = Vector2D().create_from_angle(self.direction, self.speed, return_instance = True)
        self.pos = Vector2D(x, y)
        self.color = colors.green
        # Properties neccesary to draw the ellipse in the projectile position
        self.size = 4
        self.box = (0,0,0,0)

    def update(self) :
        self.pos.add(self.velocity)
        self.box = (self.pos.x, self.pos.y, self.size, self.size)

    def draw(self) :
        stroke = 2
        pygame.draw.ellipse(self.screen, self.color, self.box, stroke)

【问题讨论】:

  • 顺便说一句:pygame 有自己的pygame.math.Vector2
  • 我知道,这个类并不是要取代那个类。将其视为具有特定游戏所需方法的 Helper 类。谢谢!
  • 使用print() 来查看变量中的值,例如self.directionVector2D().create_from_angle(),这样您就可以找出问题所在。顺便说一句,Projectile.update() 你一直在计算Vector2D().create_from_angle(),但你只能在__init__ 计算一次。
  • 试过了,没啥区别。我会把它留给构造函数,因为它看起来更有效率。谢谢你。并且输出实际上是很长的浮点数,并没有说太多:s
  • @JuanBonnett 在无法运行的情况下进行测试很难。能否提供源代码?我的主要问题是关于Vector2D().create_from_angle() 的问题。为什么该函数中有- math.pi / 2?从我在这里看到的情况来看,错误可能在那里或Ship().turn() 函数中。那里有一个对rotate() 函数的调用,但我在此处发布的源代码中没有找到其他提及它的内容。另外,我认为您应该先将 deg 添加到旋转中,然后再检查它是否在 0 到 360 之间(模函数可以摆脱 if 语句)。

标签: python vector pygame


【解决方案1】:

事实证明,错误出乎意料地出现在您的 Vector2D 类中,但不在您期望的函数中!您的 Vector2D().create_from_angle() 函数很好,并且假设 up 为 0 并且角度值顺时针增加,则创建正确的角度,即使它违反了 0 向右和逆时针增加角度值的“标准数学约定”。

真正的问题在您的Vector2D().int() 函数中,它被用作Projectile 类中draw() 函数的一部分。将float 数字转换为int 只会截断十进制值,因此您可以将int 转换与python 的round 函数一起使用,如下所示:

def int(self) :
    self.x = int(round(self.x))
    self.y = int(round(self.y))
    return self

但是,即使您进行了此修复,您也会注意到由于圆角,与您的船头相比,角度仍然略微偏离。这是因为self.pos.int() 在您的Projectile.draw() 函数下覆盖 使用不准确的四舍五入版本继续添加到Projectile.update() 函数中的当前位置。最好的解决方案可能是避免使用您的 Vector2D.int() 函数并将您的 Projectile.draw() 函数替换为:

def draw(self) :
    pos_x = int(round(self.pos.x))
    pos_y = int(round(self.pos.y))
    pygame.draw.circle(self.screen, self.color, (pos_x, pos_y), 2, 0)

上述修改将使您的射弹从您的船尖完美射击,但会导致抖动,因为位置有时会向上取整,有时会向下取整。使用修改后的Vector2D.int() 函数会导致不抖动但不准确的弹丸射击。哪个错误是最好的取决于你。让我知道这是否清楚!

【讨论】:

  • 这很有意义。当我输入不是 int() 的值时,Pygame 的 draw.circle(..) 函数会引发错误(与 draw.polygon 不同),也许这就是问题的根源,我想我应该改变形状。让我们看看。
  • 检查我编辑的问题,问题已解决,只是在您的解决方案中添加了一些内容:)!非常感谢,今天过得愉快!
  • @JuanBonnett 椭圆的好电话!现在您不必担心丑陋的舍入!
猜你喜欢
  • 2011-01-30
  • 1970-01-01
  • 2015-08-11
  • 1970-01-01
  • 1970-01-01
  • 2012-12-14
  • 2021-07-27
  • 1970-01-01
相关资源
最近更新 更多