john g
john g

Reputation: 33

making a bullet move to your cursor using pygame

I am writing a relatively complex platformer game in which a player moves using wasd and shoots with the mouse. The goal is to get the bullet to travel to the location of the mouse when it was clicked. I have code that sort of works but as the angle gets farther from 0 or 90 (straight left/right or straight up/down) the the bullets final location gets farther from the cursor. I am fairly certain the issue is simply that since the change in x and y are floating points and x,y location of the bullet cannot be floating point there is a rounding issue occurring. I have tried numerous different methods based on forum searches and all of them have the same problem. I have attached the most relevant file (pay particular attention to the bullets init class). Any advice or help would be appreciated. For the record this is just the player class NOT the main.

import pygame
import level
import platform
##import enemies
import math
pygame.init()

## sets up colors that need to be used in every part of the program
black=(0,0,0)
white=(255,255,255)
red=(255,0,0)
green=(0,255,0)
blue=(0,0,255)


class Player(pygame.sprite.Sprite):
    ## This class represents the player. It is inhariting from the Sprite class in Pygame
    window=None
    screen_width=0
    screen_height=0
    width=None
    height=None
    x_velocity=0
    y_velocity=0
    chrono_level=500
    ball_damage=0
    bomb_damage=0
    blast_radius=0
    gravity=0
    isjumping=False
    isducking=False
    level=None
    direction=None

    def __init__(self,argwindow,argsheight,argswidth,argcolor=white,argwidth=40,argheight=60,argx=0,argy=0,argball_damage=5,argbomb_damage=15,argbomb_radius=10,argchrono_level=500):
        ## Constructor. Pass in the color, width, and height
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([argwidth,argheight])
        self.image.fill(argcolor)
        self.rect=self.image.get_rect()
        ## sets up the variables inital variables
        self.window=argwindow
        self.width=argwidth
        self.height=argheight
        self.screen_height=argsheight
        self.screen_width=argswidth
        self.rect.x=argx
        self.rect.y=argy
        self.x_velocity=0
        self.y_velocity=0
        self.ball_damage=argball_damage
        self.bomb_damage=argbomb_damage
        self.bomb_radius=argbomb_radius
        self.chrono_level=argchrono_level
        self.isjumping=False
        self.isducking=False

    def update(self):
        ## check gravity
        self.calc_grav()
        ## move left or right
        self.rect.x+=self.x_velocity
        ## check for any collisions
        platform_hit_list=pygame.sprite.spritecollide(self,self.level.platform_list,False)## this is the pygame generatred collision detection builtin to the sprite class
        for platform in platform_hit_list:
            if self.x_velocity > 0:##i.e sprite was moving right
                self.rect.right = platform.rect.left ##puts the right side of the sprite flush with the left side of the platform
            elif self.x_velocity < 0:
                self.rect.left = platform.rect.right  
            self.x_velocity=0           
        ## move sprite up or down
        self.rect.y+=self.y_velocity
        ## check for any collisions
        platform_hit_list=pygame.sprite.spritecollide(self,self.level.platform_list,False)## this is the pygame generatred collision detection builtin to the sprite class
        for platform in platform_hit_list:
            if self.y_velocity > 0:## i.e. sprite is falling
                self.rect.bottom = platform.rect.top ## puts bottom of player flush with top of platform
                self.isjumping=False
            elif self.y_velocity < 0:
                self.rect.top = platform.rect.bottom
            self.y_velocity=0
        ## check direction
        pos = pygame.mouse.get_pos()
        if pos[0] > (self.rect.x+(self.width/2)): ##i.e. cursor is farther to the right then the middle of the sprite
            self.direction="Right"
        else: ##pos[0] < (self.rect.x+(self.width/2))
            self.direction="Left"
    def jump(self):
        if not self.isjumping:
            self.y_velocity=-15
            self.isjumping=True

    def calc_grav(self):
        if self.y_velocity ==0:
            self.y_velocity=1
        else:
            self.y_velocity+=self.gravity

        if self.rect.y >= self.screen_height - self.rect.height and self.y_velocity >= 0:
            self.y_velocity = 0
            self.rect.y = self.screen_height - self.rect.height
            self.isjumping=False

    def move_left(self):## called if the player hits the left arrow key or the a key
        self.x_velocity=-5

    def move_right(self):## called is the player hits the right arrow key or the d key
        self.x_velocity=5

    def stop(self):## called if the player lets up on either arrow key or the a or d key
        self.x_velocity=0

    def shoot(self,argmouse_position):
        if self.direction=="Left":
            bullet_start_x=self.rect.x
            bullet_start_y=(self.rect.y+(self.height/2))
        elif self.direction=="Right":
            bullet_start_x=(self.rect.x+self.width)
            bullet_start_y=(self.rect.y+(self.height/2))
        bullet=player_bullet(bullet_start_x,bullet_start_y,argmouse_position)
        return (bullet)


class player_bullet(pygame.sprite.Sprite):

    bullet_x=None
    bullet_y=None
    bullet_x_velocity=None
    bullet_y_velocity=None
    target_x=None
    target_y=None
    speed=10

    def __init__(self,argx,argy,argmouse_positon):
        pygame.sprite.Sprite.__init__(self)
        print "it inited"
        self.image = pygame.Surface([4, 10])
        self.image.fill(black)
        self.rect=self.image.get_rect()
        self.rect.x=argx
        self.rect.y=argy
        self.bullet_x=argx
        self.bullet_y=argy
        self.target_x=argmouse_positon[0]
        self.target_y=argmouse_positon[1]
        dx=self.target_x-self.bullet_x
        dy=self.target_y-self.bullet_y
        angle=math.atan2(dy,dx)
        print angle
        self.bullet_x_velocity=self.speed*math.cos(angle)
        self.bullet_y_velocity=self.speed*math.sin(angle)
    def update(self):
        print self.rect.x
        print self.bullet_x_velocity
        self.rect.x+=self.bullet_x_velocity
        print self.rect.x
        self.rect.y+=self.bullet_y_velocity
    def collide(self,argdisplay_width,argdisplay_height,argplatform_list):
        Platform_hit_list=pygame.sprite.spritecollide(self, argplatform_list, False)
        if len(Platform_hit_list) > 0:
            return True
        elif self.rect.x > argdisplay_width or self.rect.x < 0:
            return True
        elif self.rect.y > argdisplay_height or self.rect.y < 0:
            return True
        else:
            return False

Upvotes: 2

Views: 1490

Answers (1)

Fahim Shahriar
Fahim Shahriar

Reputation: 36

I have written a similar game mechanic, where instead of a bullet I could shoot any projectile, of any size, of any sprite. What I did was add my current position to number of pixels I have to move to (defined as vx, vy below). I found the number of pixels i have to move by dividing the differences in the axis, by distance, and the multiplying a speed ( usually 10). Here is the projectile class below(the mask stuff is so bullets don't go into the buildings):

class projectile:
    """an image goes towards the target from starting location"""

    def __init__(self, xorg, yorg, x, y, sprite, speed):
        self.x = xorg
        self.y = yorg
        self.gotox = x
        self.gotoy = y
        self.sprite = sprite
        self.xsize = self.sprite.get_width()
        self.ysize = self.sprite.get_height()
        self.rect = Rect(self.x, self.y, self.xsize, self.ysize)

        # divided by 2, because we want the middle of the projectile to goto the destination, not the edge
        self.gotox -= self.xsize / 2
        self.gotoy -= self.ysize / 2

        self.speed = speed

        #differance in the x and y axis of destination to current position
        self.dx = self.gotox - self.x
        self.dy = self.gotoy - self.y

        self.slope = (self.dy / max(1, self.dx))
        self.gotox += self.gotox * self.slope
        self.gotoy += self.gotoy * self.slope
        self.dist = max(1, hypot(self.dx, self.dy))
        self.state = "alive"
        self.rect = Rect(self.x, self.y, self.xsize, self.ysize)

    def move(self):
        """moves based on where the target was during time of shooting 
        untill it hits targer, or hits a wall"""
        global currentMask
        dist = max(1, hypot(self.dx, self.dy))

        # found the num of pixels I have to move (speed is usually 10) 
        self.vx = self.speed * (self.dx / self.dist)
        self.vy = self.speed * (self.dy / self.dist)

        if currentMask.get_at((int(self.x + self.vx), int(self.y + self.vy))) != (0, 0, 0) and currentMask.get_at(
                (int(self.x + self.vx + self.xsize), int(self.y + self.vy + self.ysize))) != (0, 0, 0) and int(
                            self.y + self.vy + self.ysize) <= 800 - self.ysize and int(
                    self.y + self.vy) >= 0 + self.ysize and int(self.x + self.vx) >= self.xsize and int(
                            self.x + self.vx + self.xsize) <= 1200 - self.xsize:
            # added the num of pixels i have to move, to my current postition
            self.x += self.vx
            self.y += self.vy
        else:
            self.state = "dead"
        self.rect = Rect(self.x, self.y, self.xsize, self.ysize)
        screen.blit(self.sprite, (self.x, self.y))

Upvotes: 1

Related Questions