Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I've been working on movement for the enemy in my game for a day or so and can't seem to get it working
the problem is it will chase the character for a little bit until it hits x or y zero and will stick there forever, and sometimes it will just stick in place when it hits the player
any help will be greatly appreciated
import pygame
import math
ALPHA = (0, 255, 0)
path = "data/chr/squid.png"
def findDist(coords1, coords2):
try:
return math.sqrt(pow(coords1[0] - coords2[0], 2) + pow(coords1[1] - coords2[1], 2))
except:
print("invalid dist input, ignoring")
class Squid(pygame.sprite.Sprite):
#player object
def __init__(self, X, Y):
pygame.sprite.Sprite.__init__(self)
self.speed = .01
self.images = []
for i in range(1, 5):
img = pygame.image.load(path).convert()
img.convert_alpha() # optimise alpha
img.set_colorkey(ALPHA) # set alpha
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.rect.x = X
self.rect.y = Y
def chasePlayer(self, player):
dx, dy = player.rect.x - self.rect.x, player.rect.y - self.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self.rect.x += dx * self.speed
self.rect.y += dy * self.speed
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle.
x, y = # floating point coordinates
rect.topleft = round(x), round(y)
Read about Classes. If you want to spawn multiple enemies, you must create multiple Instance Objects of the Squid class. e.g.:
enemies = []
for _ in range(5):
x = random.randrange(screen_width)
y = random.randrange(screen_height)
enemies.append(Squid(x, y))
Squid class:
class Squid(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.speed = .01
self.image = pygame.image.load(path).convert()
self.image.convert_alpha() # optimise alpha
self.image.set_colorkey(ALPHA) # set alpha
self.x = x
self.y = y
self.rect = self.image.get_rect(topleft = (round(x), round(y)))
def chasePlayer(self, player):
dx, dy = player.rect.x - self.rect.x, player.rect.y - self.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self.x += dx * self.speed
self.y += dy * self.speed
self.rect.x = round(self.x)
self.rect.y = round(self.y)
Related
This question already has answers here:
How to make smooth movement in pygame
(2 answers)
How can i make a block follow another block in pygame [duplicate]
(2 answers)
Closed 2 years ago.
So I followed the answers in another question asked on StackOverflow but it seems that I have missed something. I went ahead after reading the answer and copied the code and adjusted it to my variables and class names.
The following is the error code that Idle gives me:
Traceback (most recent call last):
File "D:\Programme (x86)\Python\Games\Zombie Game\Zombie Game_Test1.py", line 133, in <module>
Zombie.move_towards_Char(Char)
TypeError: move_towards_Char() missing 1 required positional argument: 'Char'
This is where I looked:
How to make an enemy follow the player in pygame?
import pygame
import turtle
import time
import math
import random
import sys
import os
pygame.init()
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
BGColor = (96,128,56)
ZColor = (225,0,0)
PColor = (0,0,255)
MOVE = 2.5
size = (1920, 1080)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zombie Game")
class Char(pygame.sprite.Sprite):
def __init__(self, color, pos, radius, width):
super().__init__()
self.image = pygame.Surface([radius*2, radius*2])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.circle(self.image, color, [radius, radius], radius, width)
self.rect = self.image.get_rect()
def moveRightP(self, pixels):
self.rect.x += pixels
pass
def moveLeftP(self, pixels):
self.rect.x -= pixels
pass
def moveUpP(self, pixels):
self.rect.y -= pixels
pass
def moveDownP(self, pixels):
self.rect.y += pixels
pass
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.rect.center = pos
def move_towards_Char(self2, Char):
dx, dy = self2.rect.x - Char.rect.x, self2.rect.y - Char.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self2.rect.x += dx * self2.speed
self2.rect.y += dy * self2.speed
def moveRightZ(self2, pixels):
self2.rect.x += pixels
pass
def moveLeftZ(self2, pixels):
self2.rect.x -= pixels
pass
def moveUpZ(self2, pixels):
self2.rect.y -= pixels
pass
def moveDownZ(self2, pixels):
self2.rect.y += pixels
pass
all_sprites_list = pygame.sprite.Group()
playerChar = Char(PColor, [0, 0], 15, 0)
playerChar.rect.x = 960
playerChar.rect.y = 505
all_sprites_list.add(playerChar)
carryOn = True
clock = pygame.time.Clock()
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = pygame.time.get_ticks() + 10000
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = 10000
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
for zombie in zombie_list:
screen.blit(zombie.image,zombie.rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
The major issue is, that you do the zombie movement calculations with integral data types. If the movement of a zombie is 1 pixel and the movement is diagonal, then the x and y component of the movement is < 1. Using an integral data type, this may results in 0 movement, because of truncating to int. Note the members of pygame.Rect are integral values.
You've to switch to floating point values to solve the issue. Use pygame.math.Vector2 to do the calculations.
Add a member pos of type Vector2 to the class Zombie which stores the floating point position of the zombie:
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.speed = 1
self2.pos = pygame.Vector2(pos[0], pos[1])
# [...]
Add a new method draw to the class Zombie, which draws (blit) a zombie at the position pos:
class Zombie(pygame.sprite.Sprite):
# [...]
def draw(self2):
self2.rect.center = (int(round(self2.pos.x)), int(round(self2.pos.y)))
screen.blit(self2.image, self2.rect)
Do the calculation of the movement of the zombie based on Vector2. Ensure that the distance between the player and the zombie is greater than 0 and that the zombie does not step over of the position of the player (min(len, self2.speed)):
class Zombie(pygame.sprite.Sprite):
# [...]
def move_towards_Char(self2, Char):
deltaVec = pygame.Vector2(Char.rect.center) - self2.pos
len = deltaVec.length()
if len > 0:
self2.pos += deltaVec/len * min(len, self2.speed)
Call the methods move_towards_Char and draw for each zombie, in the main loop of the application:
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
# update all the positions of the zombies
for zombie in zombie_list:
zombie.move_towards_Char(playerChar)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
# draw all the zombies
for zombie in zombie_list:
zombie.draw()
pygame.display.flip()
clock.tick(60)
L{Zombie.move_towards_Char} is a self method. You need to create object of Zombie class passing the required args mentioned in L{Zombie.init}.
Something like below:
zm = Zombie(color=<color>, pos=<pos>, radius=<radius>, width=<width>)
zm.move_towards_Char(Char)
I don't understand why the sprite collision detection is not taking the image rotation into account.
I tried different functions but they didn't work out for me.
CarSprites.py:
import pygame, math, random
class CarSprite(pygame.sprite.Sprite):
MIN_FORWARD_SPEED = 5
ACCELERATION = 2
TURN_SPEED = 5
IS_DUMMY = False
def __init__(self, image, position, direction):
pygame.sprite.Sprite.__init__(self)
self.src_image = pygame.transform.scale(pygame.image.load(image), (51, 113))
self.position = position
self.rect = self.src_image.get_rect()
self.rect.center = self.position
self.speed = 0
self.direction = direction
self.k_left = self.k_right = self.k_down = self.k_up = 0
def update(self, deltat):
# SIMULATION
#speed
self.speed += (self.k_up + self.k_down)
if self.speed < self.MIN_FORWARD_SPEED:
self.speed = self.MIN_FORWARD_SPEED
self.speed += (self.k_up + self.k_down)
if self.speed > self.MIN_FORWARD_SPEED * 2:
self.speed = self.MIN_FORWARD_SPEED * 2
#direction
self.direction += (self.k_right + self.k_left)
x, y = self.position
rad = self.direction * math.pi / 180
x += -self.speed*math.sin(rad)
y += -self.speed*math.cos(rad)
self.position = (x, y)
self.image = pygame.transform.rotate(self.src_image, self.direction)
self.rect = self.image.get_rect()
self.rect.center = self.position
#Emulate friction with road and wind
if self.speed > self.MIN_FORWARD_SPEED :
self.speed += -0.1
class DummyCarSprite(pygame.sprite.Sprite):
#MIN_FORWARD_SPEED = 5
#MIN_REVERSE_SPEED = 10.1
#MAX_FORWARD_SPEED_ABOVE_MIN = 5
#ACCELERATION = 2
#TURN_SPEED = 5
def __init__(self, image, position, direction):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(pygame.image.load(image), (51, 113))
self.position = position
self.rect = self.image.get_rect()
self.rect.center = self.position
self.speed = 0
self.direction = direction
self.k_left = self.k_right = self.k_down = self.k_up = 0
if random.randint(0,1) == 1 :
self.direction = self.direction + 180
game.py
def GetDummyCars() :
allDummyCars = [
#Row1
#DummyCarSprite(getCarImage(), (211.9915431212928, 209.36603413022453), 180),
#DummyCarSprite(getCarImage(), (268.9915431212928, 209.36603413022453), 180),
DummyCarSprite(getCarImage(), (325.9915431212928, 209.36603413022453), 180),
DummyCarSprite(getCarImage(), (382.9915431212928, 209.36603413022453), 180)
#etc. etc.
]
dummyCars = []
for dummyCar in allDummyCars :
if random.randint(0,1) == 1 :
dummyCars.append(dummyCar)
return pygame.sprite.RenderPlain(*dummyCars)
playerCar = CarSprite(playerCarImagePath, (1550, 100), 90)
playerCar_group = pygame.sprite.RenderPlain(playerCar)
dummyCar_group = GetDummyCars()
#collisions with dummy cars
dummyCarCollisions = pygame.sprite.groupcollide(playerCar_group, dummyCar_group)
if dummyCarCollisions != {}:
lose_condition = True
playerCar.src_image = pygame.image.load('images/collision.png')
seconds = 0
playerCar.speed = 0
playerCar.MIN_FORWARD_SPEED = 0
playerCar.MAX_FORWARD_SPEED_ABOVE_MIN = 0
playerCar.k_right = 0
playerCar.k_left = 0
I would like to find a way to detect collision between the sprites in the 2 car groups, or collision between the player sprite and the dummycar_group (each would work out for me) that takes the rotation of the image into account.
What happens now is when I steer the car, the car image rotates but it looks like the collision detection doesn't see that.
Is there a better function i can use that could handle this?
My full source code: dropbox
I found this question very interesting and fun to work on and fix! Thanks for posting!
I have found the problem and it is rather unfortunate. Your code runs perfectly from what I have seen. The problem is that pygame uses rectangles for collision detection which are not precise enough.
You are applying the rotation to the image but that just makes it bigger and less accurate. I have highlighted the problem with the addition of rendiering debug lines in the GameLoop function.
# draw some debug lines
pygame.draw.rect(screen, (255, 0, 0), playerCar.rect, 1)
for dummyCar in dummyCar_group.sprites():
pygame.draw.rect(screen, (0, 0, 255), dummyCar.rect, 1)
Add these lines in and you shall see for yourself.
The only solution that I can think of is to add in the functionality to use polygons for collision detection yourself.
The way I would implement this is to:
Stop using the rect attribute of all Sprites for collision detection and stop using any methods for collision detection that use the underlying Rects, e.g pygame.sprite.spritecollide().
add a pointlist field to all sprites that need it which will store all the points of the polygon
Implement your own function that takes in two lists of points and returns if they overlap
I hope that this answer helped you and if you have any further questions please feel free to post a comment below!
I just checked here to make sure this question was allowed, and it seems that it is so here I go:
I am currently making a 2D physics engine as a small project. I have a class called circle which has properties such as radius, rotation, position, and velocity:
class circle():
def __init__(self, radius = 10, r = 0.0, x = 0, y = 0, Vr = 0, Vx = 0, Vy = 0):
self.radius = radius
self.r = r
self.x = x
self.y = y
self.Vr = Vr
self.Vx = Vx
self.Vy = Vy
The class has a method called CheckCollisions(), which checks if the distance between its centre and another circle's centre is less than the sum of their radii:
def CheckCollisions(self):
for c in circles:
distance = math.sqrt((c.x - self.x)*(c.x - self.x) + (c.y - self.y)*(c.y - self.y))
if distance < self.radius + c.radius:
print('!')
else:
print('')
The idea is that on detecting the collision, forces can be applied as vectors to each object as a response to the impact.
When my code runs, I see constant exclamation marks appearing in the shell, despite the circles not colliding. What is causing this? Perhaps something in my calculation of distance is incorrect?
Full code:
import pygame, random, math
from pygame.locals import*
# set up pygame window
(width, height) = (1000, 800)
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption('Impulse Physics v0.1 BETA')
pen = pygame.image.load('Pen.png').convert()
background = (0, 0, 0)
class circle():
def __init__(self, radius = 10, r = 0.0, x = 0, y = 0, Vr = 0, Vx = 0, Vy = 0):
self.radius = radius
# position and rotation
self.r = r
self.x = x
self.y = y
# velocity
self.Vr = Vr
self.Vx = Vx
self.Vy = Vy
def CheckCollisions(self):
for c in circles:
# use pythagoras to find direct distance between centres
distance = math.sqrt((c.x - self.x)*(c.x - self.x) + (c.y - self.y)*(c.y - self.y))
if distance < self.radius + c.radius:
print('!')
else:
print('')
def Move(self):
# apply slight "air resistance"
self.Vx = self.Vx * 0.9999
# gravity. REMEMBER y axis is inverted in pygame!
self.Vy = self.Vy + 0.15
# move object
self.x = self.x + self.Vx
self.y = self.y + self.Vy
self.r = self.r + self.Vr
self.CheckCollisions()
# check if colliding with the sides of the window
if self.y + self.radius > height:
self.Vy = self.Vy * -0.98
self.y = self.y + self.Vy
if (self.x + self.radius > width) or (self.x - self.radius < 0):
self.Vx = self.Vx * -0.98
self.x = self.x + self.Vx
def Render(self):
penX = self.x
penY = self.y
penR = self.r
screen.blit(pen, (penX, penY))
# draw the radius of the circle
for counter in range(self.radius):
penX = self.x + (math.sin(penR) * counter)
penY = self.y - (math.cos(penR) * counter)
screen.blit(pen, (penX, penY))
# draw the circumference of the circle
for counter in range(self.radius * 20):
penR = counter * (360 / self.radius * 20)
penX = self.x + (math.sin(penR) * self.radius)
penY = self.y + (math.cos(penR) * self.radius)
screen.blit(pen, (penX, penY))
circles = []
#create objects here
c1 = circle(100, 0, 400, 400, 0.1, 4)
circles.append(c1)
c2 = circle(50, 0, 50, 50, 0.08, 10)
circles.append(c2)
c3 = circle(10, 0, 300, 200, 0.02, -3)
circles.append(c3)
running = True
while running:
screen.fill(background)
for obj in circles:
obj.Move()
obj.Render()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
pygame.quit()
In short: a circle collides with itself. The reason is simply that the circles list contains [c1,c2,c3] and thus checks are done against the circles themselves.
Now for c1 you check whether there is a collision so it iterates over the circles and the first thing it checks is whether it collides with itself (since c1 is the first element in the list). And obviously it does (your test looks if the distance is less than the sum of the circles radiuses, but the distance is zero). If none of the circles collide, there will thus be three exclamation marks (one for each circle).
You can resolve this error by performing a reference equality check first:
def CheckCollisions(self):
for c in circles:
if c is not self:
distance = math.sqrt((c.x - self.x)*(c.x - self.x) + (c.y - self.y)*(c.y - self.y))
#...
Ok, I've been working all day on this and I haven't found the logic.
I wanna make a classic style asteroids game, and I'm starting with the spaceship.
What I did was draw some lines with the shape of a spaceship:
import pygame
import colors
from helpers import *
class Ship :
def __init__(self, display, x, y) :
self.x = x
self.y = y
self.width = 24
self.height = 32
self.color = colors.green
self.rotation = 0
self.points = [
#A TOP POINT
(self.x, self.y - (self.height / 2)),
#B BOTTOM LEFT POINT
(self.x - (self.width / 2), self.y + (self.height /2)),
#C CENTER POINT
(self.x, self.y + (self.height / 4)),
#D BOTTOM RIGHT POINT
(self.x + (self.width / 2), self.y + (self.height / 2)),
#A TOP AGAIN
(self.x, self.y - (self.height / 2)),
#C A NICE LINE IN THE MIDDLE
(self.x, self.y + (self.height / 4)),
]
def move(self, strdir) :
dir = 0
if strdir == 'left' :
dir = -3
elif strdir == 'right' :
dir = 3
self.points = rotate_polygon((self.x, self.y), self.points, dir)
def draw(self, display) :
stroke = 2
pygame.draw.lines(display, self.color, False, self.points, stroke)
The ship looks like this:
Now important things to know:
The tuple (self.x, self.y) is the middle of the spaceship.
Using this function I managed to rotate (spin) it on command using the keys A and D
def rotate_polygon(origin, points, angle) :
angle = math.radians(angle)
rotated_polygon = []
for point in points :
temp_point = point[0] - origin[0] , point[1] - origin[1]
temp_point = (temp_point[0] * math.cos(angle) - temp_point[1] * math.sin(angle),
temp_point[0] * math.sin(angle) + temp_point[1] * math.cos(angle))
temp_point = temp_point[0] + origin[0], temp_point[1] + origin[1]
rotated_polygon.append(temp_point)
return rotated_polygon
The problem is: How can I make it move forward or backwards in the direction the spaceship is pointing?
OR
How can I update the self.x and self.y values and update them inside the self.points list and preserve rotation?
The easiest, most general way to deal with movement and rotation would to use some vector math (can apply to 3D graphics as well). You could keep a 2D vector representing the forward direction of your ship. For example, if your ship starts facing upwards and your (0,0) coordinate is the top left. You could do.
self.forward = Vector2D(0, -1) # Vector2D(x, y)
When you rotate you must rotate this vector. You can rotate using the following.
self.forward.x = self.forward.x * cos(angle) - self.forward.y * sin(angle)
self.forward.y = self.forward.x * sin(angle) + self.forward.y * cos(angle)
Then when you want to move the ship you can transform the ship points relative to this vector. For example.
self.x += forward.x * velocity.x
self.y += forward.y * velocity.y
I would highly recommend you write a little Vector2D class which can do some of the basic operations, e.g. dot, cross, mult, add, sub, normalize, etc.
If you are familiar with matrices then these operations can become easier if you implement them using matrices instead of a system of linear equations.
It seems to me that you should be able to simply do the following.
def updatePosition(self, dx, dy):
self.x += dx
self.y += dy
newPoints = []
for (x,y) in self.points:
newPoints.append((x+dx, y+dy))
self.points = newPoints
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
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))