Reputation: 33
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
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