首页 > 解决方案 > 子弹看起来不像是从枪口里出来的 Pygame

问题描述

在此处输入图像描述我遇到了一个问题,我的子弹看起来不像是从我的枪里出来的,它们看起来像是从玩家身体里出来。左侧向上射得好,向下射得不好视频

***我想说的是我怎样才能让我的枪在我的鼠标位置精确旋转***

我尝试将我的枪调整到 120,但发生的情况是一切都适用于右侧而不是左侧VIDEO,因为你可以看到它只是小故障


    def draw(self,drawX,drawY):
 
        self.rect.topleft =  (drawX,drawY)
 
            # the guns hitbox
 
            # rotatiing the gun
        dx = self.look_at_pos[0] - self.rect.centerx
        dy = self.look_at_pos[1] - self.rect.centery 
            
        angle = (190/math.pi) * math.atan2(-dy, dx)
      
        gun_size = self.image.get_size()
        pivot = (8, gun_size[1]//2)
            
 
        blitRotate(window, self.image, self.rect.center, pivot, angle)

        if((angle > 90 or angle < -90) and self.gunDirection != "left"):
            self.gunDirection = "left"
            self.image = pygame.transform.flip(self.image, False, True)
        if((angle < 90 and angle > -90) and self.gunDirection != "right"):
            self.gunDirection = "right"
            self.image = pygame.transform.flip(self.image, False, True)





我的全枪课

 
class handgun():
    def __init__(self,x,y,height,width,color):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.color = color
        self.rect = pygame.Rect(x,y,height,width)
 
 
        # LOL THESE IS THE HAND
        self.shootsright = pygame.image.load("hands.png")
        self.image = self.shootsright
        self.rect  = self.image.get_rect(center = (self.x, self.y))
        self.look_at_pos = (self.x, self.y)
 
        self.isLookingAtPlayer = False
        self.look_at_pos = (x,y)
            
 
 
 
        self.hitbox = (self.x + -18, self.y, 46,60)

 
        self.gunDirection = "right"
    def draw(self,drawX,drawY):
 
        self.rect.topleft =  (drawX,drawY)
 
            # the guns hitbox
 
            # rotatiing the gun
        dx = self.look_at_pos[0] - self.rect.centerx
        dy = self.look_at_pos[1] - self.rect.centery 
            
        angle = (120/math.pi) * math.atan2(-dy, dx)
      
        gun_size = self.image.get_size()
        pivot = (8, gun_size[1]//2)
            
 
        blitRotate(window, self.image, self.rect.center, pivot, angle)

        if((angle > 90 or angle < -90) and self.gunDirection != "left"):
            self.gunDirection = "left"
            self.image = pygame.transform.flip(self.image, False, True)
        if((angle < 90 and angle > -90) and self.gunDirection != "right"):
            self.gunDirection = "right"
            self.image = pygame.transform.flip(self.image, False, True)




    def lookAt( self, coordinate ):
            
        self.look_at_pos = coordinate
 
 
 
white = (255,255,255)
handgun1 = handgun(300,300,10,10,white)




how my images are blitted





```def blitRotate(surf, image, pos, originPos, angle):
 
        # calcaulate the axis aligned bounding box of the rotated image
    w, h = image.get_size()
    sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle)) 
    min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
 
        # calculate the translation of the pivot 
    pivot        = pygame.math.Vector2(originPos[0], -originPos[1])
    pivot_rotate = pivot.rotate(angle)
    pivot_move   = pivot_rotate - pivot
 
        # calculate the upper left origin of the rotated image
    origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
 
        # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)
 
        # rotate and blit the image
    surf.blit(rotated_image, origin)
 

我想我想说的是我怎样才能让我的枪完全在我的鼠标位置旋转而没有任何问题

我的完整代码脚本

标签: pythonpygame

解决方案


一种方法是计算出枪尖移动通过的弧相对于其x,y坐标的椭圆。使用它来创建一个“查找表

查找表,当索引为 0→360° 时,对于给定的角度,它给出了一对添加到“手臂”原点位置的偏移量。这个新点加在一起就是枪尖的坐标。例如,假设枪在 30°,我们访问end_of_gun_lookup[ 30 ],然后返回( 12, -6 ),这对应于“手臂”精灵位置和枪尖之间的差异。

检查您的代码,角度似乎从大约-120120. 显然我们不能对查找表有一个负索引,所以我们只需将所有内容移动120end_of_gun_lookup[ 0 ]对于 angle -120end_of_gun_lookup[ 1 ]对于 angle-119等也是如此。

所以现在当你开始定位子弹时,它应该在“arm”位图上创建origin,加上end_of_gun_lookup[ 120 + round( angle ) ]

但!我们如何创建该查找表?

一种方法是在位图旋转时以编程方式“跟随”枪尖。一旦找到枪尖,立即将该位置存储在查找表中以获得已知角度。

为了找到枪的尖端,我修改了“手臂”位图,在枪的末端有一个亮绿色的​​像素块 (3x3)。这可能是位图中尚未使用的任何颜色,但我选择了 green ( 8, 255, 0 )

超绿像素枪

然后我们创建函数,在每次旋转过程中,费力地在旋转位图的每个像素中寻找绿色像素。这很慢,而且不是您在玩游戏时想要做的事情。

旋转臂

这就是那个功能。这end_of_gun_lookup是最终的查找表。None首先,我们为每个可能的角度填充“无值” 。

# Create empty look-up point for green-pixel offsets
SUPA_GREEN = ( 8, 255, 0 )
end_of_gun_lookup = [ None ] * 360

findGreenPixels()只要能够找到像素位置,该函数就会填充这个全局列表:

def findGreenPixels( origin, image, rotation_angle ):
    global end_of_gun_lookup
    
    result = None
    # Bitmap offset 
    origin_x, origin_y = origin
    # find the Super Green pixel at the end of the gun.
    # very slow, and inefficient 
    width = image.get_rect().width
    height = image.get_rect().height
    for y in range( height ):
        for x in range( width ):
            pixel_colour = image.get_at( ( x, y ) )
            if ( pixel_colour == SUPA_GREEN ):
                #print( "GREEN AT %d -> %d,%d" % ( rotation_angle, x, y ) )
                result = ( round( origin_x - x ), round( origin_y - y ) )
                # results go from -120 -> 120, so offset
                # before storing the point.
                # Distance is relative to bitmap orgin too
                end_of_gun_lookup[ round( rotation_angle ) + 120 ] = ( result )
            if ( result != None ):
                break
        if ( result != None ):
            break
    return result

它基本上遍历每个像素,寻找那个绿色。如果找到,则填充查找表。我使用了一个 3x3 的像素块,因为在图像旋转期间,像素被着色和模糊,并且像素块具有更好的变化以保持完全相同的颜色。

在程序结束时,我们转储查找表:

pygame.quit()

### PRINT THE LOOKUP TABLE
end_of_gun_lookup = fillLookupHoles( end_of_gun_lookup )
print( "end_of_gun_lookup = "+ str( end_of_gun_lookup ) )

在我的测试过程中,无论我移动鼠标的速度有多慢,有时表格中都会出现未定义的位置。虽然我只管理过一张完美的桌子。无论如何,我添加了一些代码,使用中点线算法来估计单个缺失值的位置。这清理了那些洞。显然你不能在表格的末端生成点,这些点保持为None. 也许代码需要处理这些,也许它们永远不会发生。

这给了我这样的结果:

end_of_gun_lookup = [(108, 160), (109, 162), (110, 163), (111, 163), (111, 163), (112, 164), (111, 166), (113, 167) , (114, 168), (114, 170), (115, 171), (116, 173), (116, 174), (118, 175), (118, 177), (120, 177), ( 120, 178), (121, 180), (122, 182), (124, 183), (125, 185), (95, 181), (126, 188), (129, 189), (130, 191), (130, 193), (133, 196), (103, 194), (104, 197), (137, 199), (107, 201), (106, 199), (137, 197) , (105, 196), (104, 194), (101, 193), (101, 191), (101, 189), (130, 183), (99, 186), (99, 184), ( 99, 183), (97, 182), (98, 180), (98, 178), (95, 179), (96, 177), (96, 175), (94, 174), (95, 172), (94, 171), (94, 170), (94, 170), (93, 168), (94, 166), (95, 165), (95, 164), (95, 163) , (95, 162), (94, 161), (94, 160), (95, 159), (95, 158), (96, 157), (97, 156), (98, 155), ( 98, 154), (98, 155), (98, 154), (98, 152), (101, 151), (101, 151), (101,150), (102, 150), (102, 149), (104, 148), (105, 148), (106, 148), (107, 146), (108, 147), (109, 146) , (110, 146), (110, 146), (112, 146), (114, 145), (115, 145), (117, 144), (117, 144), (119, 145), ( 119, 144), (122, 145), (123, 144), (126, 146), (126, 146), (128, 145), (129, 145), (131, 146), (134, 145), (134, 145), (136, 147), (138, 147), (140, 147), (142, 147), (144, 148), (144, 148), (148, 149) , (149, 150), (151, 150), (153, 150), (154, 152), (158, 151), (159, 152), (161, 153), (163, 153), ( 166, 154), (169, 156), (170, 156), (172, 158), (175, 158), (177, 159), (179, 160), (178, 159), (176, 157), (174, 156), (173, 154), (171, 154), (169, 152), (168, 149), (164, 149), (163, 149), (163, 147) , (161, 146), (160, 144), (159, 143), (155, 142), (154, 141), (154, 141), (154, 140), (151, 138), ( 151, 138), (148, 138), (146, 137), (145, 136), (144, 135),(144, 134), (143, 134), (142, 133), (139, 133), (140, 132), (138, 132), (137, 131), (136, 131), (135) , 130), (134, 130), (133, 129), (133, 129), (132, 130), (130, 130), (130, 129), (129, 129), (129, 129) ), (128, 129), (127, 128), (128, 129), (126, 129), (126, 129), (125, 129), (125, 130), (124, 130), (124, 130), (123, 131), (124, 131), (124, 132), (123, 132), (122, 132), (122, 134), (122, 134), (123) , 134), (122, 134), (122, 135), (122, 136), (122, 137), (123, 139), (122, 139), (122, 140), (123, 140) ), (123, 142), (123, 142), (123, 143), (124, 145), (124, 146), (124, 147), (125, 147), (125, 149), (126, 151), (126, 151), (127, 153), (128, 154), (129, 156), (129, 157), (129, 158), (130, 159), (130) , 160), (132, 163), (133, 165), (134, 165), (135, 167), (136, 169), (105, 169), (137, 172), (107, 174) ), (137, 172), (103, 171), (103, 169), (102, 167), (131,163), (98, 165), (99, 162), (96, 160), (96, 159), (93, 159), (93, 157), (93, 155), (93, 154) , (91, 152), (89, 153), (89, 151), (89, 149), (87, 147), (86, 146), (85, 146), (84, 145), ( 85, 143), (84, 143), (82, 142), (83, 140), (82, 139), (83, 139), (82, 139), (82, 137), (82, 136),无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无]

最后的所有那些Nones 可能不会发生。至少不是通过鼠标旋转。

因此,一旦定义了此查找表,就可以将其合并到代码中,并删除(或注释掉)生成的代码。

编辑:

end_of_gun_lookup因此,将上一步生成的查找表的定义添加到您的源中。然后修改blitRotate()使用它。

def blitRotate(surf, image, pos, originPos, angle):

    ... 

    # use lookup table to find the end-of gun at this angle
    x_origin, y_origin = origin
    x_offset, y_offset = end_of_gun_lookup[ round( angle ) + 120 ]
    final_pos = ( x_origin + x_offset, y_origin + y_offset )

    # rotate and blit the image
    surf.blit( rotated_image, final_pos )

参考代码:(使用上面的静态位图nug.png

import pygame
import random
import math

# Window size
WINDOW_WIDTH    = 400
WINDOW_HEIGHT   = 400
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
DARK_BLUE = (   3,   5,  54)


# Create empty look-up point for green-pixel offsets
SUPA_GREEN = ( 8, 255, 0 )
end_of_gun_lookup = [ None ] * 360

def midpoint( point_a, point_b ):
    """ Use the midpoint-line formula to return the point between 
        point_a and point_b """
    mid_x = round( ( point_a[0] + point_b[0] ) / 2 ) 
    mid_y = round( ( point_a[1] + point_b[1] ) / 2 ) 
    return ( mid_x, mid_y )

def fillLookupHoles( coord_list ):
    """ Find any gaps in the lookup table, by finding the mid-point line
        pixel between the two points, giving an estimated position """
    for i in range( 1, len ( coord_list )-1 ):  # we can't fix end-points, ignore first & last
        before = coord_list[ i-1 ]
        after  = coord_list[ i+1 ]
        if ( coord_list[ i ] == None and before != None and after != None ):
            coord_list[ i ] = midpoint( before, after )
            print( "Filled hole at angle %d" % ( i - 120 ) )
    return coord_list

def findGreenPixels( origin, image, rotation_angle ):
    global end_of_gun_lookup

    result = None
    # Bitmap offset
    origin_x, origin_y = origin
    # find the Super Green pixel at the end of the gun.
    # very slow, and inefficient
    width = image.get_rect().width
    height = image.get_rect().height
    for y in range( height ):
        for x in range( width ):
            pixel_colour = image.get_at( ( x, y ) )
            if ( pixel_colour == SUPA_GREEN ):
                #print( "GREEN AT %d -> %d,%d" % ( rotation_angle, x, y ) )
                result = ( round( origin_x - x ), round( origin_y - y ) )
                # results go from -120 -> 120, so offset
                # before storing the point.
                # Distance is relative to bitmap orgin too
                end_of_gun_lookup[ round( rotation_angle ) + 120 ] = ( result )
            if ( result != None ):
                break
        if ( result != None ):
            break
    return result



def blitRotate(surf, image, pos, originPos, angle):
 
    # calcaulate the axis aligned bounding box of the rotated image
    w, h = image.get_size()
    sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle)) 
    min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
 
        # calculate the translation of the pivot 
    pivot        = pygame.math.Vector2(originPos[0], -originPos[1])
    pivot_rotate = pivot.rotate(angle)
    pivot_move   = pivot_rotate - pivot
 
        # calculate the upper left origin of the rotated image
    origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
 
        # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)

    end_of_gun_coord = findGreenPixels( origin, rotated_image, angle ) 
 
        # rotate and blit the image
    surf.blit(rotated_image, origin)
 


 
class handgun():
    def __init__(self,x,y,height,width,color):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.color = color
        self.rect = pygame.Rect(x,y,height,width)
 
        # LOL THESE IS THE HAND
        self.shootsright = pygame.image.load("nug.png")
        self.image = self.shootsright
        self.rect  = self.image.get_rect(center = (self.x, self.y))
        self.look_at_pos = (self.x, self.y)
 
        self.isLookingAtPlayer = False
        self.look_at_pos = (x,y)
        self.hitbox = (self.x + -18, self.y, 46,60)

        self.gunDirection = "right"

    def draw(self,drawX,drawY):
        self.rect.topleft =  (drawX,drawY)
 
        # the guns hitbox
        # rotatiing the gun
        dx = self.look_at_pos[0] - self.rect.centerx
        dy = self.look_at_pos[1] - self.rect.centery 
            
        angle = (120/math.pi) * math.atan2(-dy, dx)
        gun_size = self.image.get_size()
        pivot = (8, gun_size[1]//2)
 
        blitRotate(window, self.image, self.rect.center, pivot, angle)

        if((angle > 90 or angle < -90) and self.gunDirection != "left"):
            self.gunDirection = "left"
            self.image = pygame.transform.flip(self.image, False, True)
        if((angle < 90 and angle > -90) and self.gunDirection != "right"):
            self.gunDirection = "right"
            self.image = pygame.transform.flip(self.image, False, True)

    def lookAt( self, coordinate ):
        self.look_at_pos = coordinate
 
 

 
white = (255,255,255)
handgun1 = handgun(300,300,10,10,white)
        



### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("Track Path of Green")


### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.MOUSEBUTTONUP ):
            # On mouse-click
            pass

    handgun1.direction = "right"

    # gun rotation
    mousex, mousey = pygame.mouse.get_pos()
    if not handgun1.isLookingAtPlayer:
        handgun1.lookAt((mousex, mousey))


    # Update the window, but not more than 60fps
    window.fill( DARK_BLUE )
    handgun1.draw( 200, 200 )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()

### PRINT THE LOOKUP TABLE
end_of_gun_lookup = fillLookupHoles( end_of_gun_lookup )
print( "end_of_gun_lookup = "+ str( end_of_gun_lookup ) )

推荐阅读