Is there any way I can make my code easier to understand and simpler? [closed] - python

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am wondering if by any chance there is a way to optimise this portion of my code. Regarding the pathing system and the redraw section. I find it really difficult to follow through it. Even changing the whole thing into a more understanding format will suit me. Thank you very much.
class wolf(object): #creating the wolf class
wolf_right = ['pics/WR.png'] + ['pics/WR' + str(i) + '.png' for i in range(2, 18)] #identifies the right wolf pics
wolf_left = ['pics/WL.png'] + ['pics/WL' + str(i) + '.png' for i in range(2, 18)] #identifies the left wolf pics
run_right = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_right] #loads the right wolf pics
run_left = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_left] #load the left wolf pics
def __init__(self, x, y, width, height, finish): #initialising the object wolf
self.x = x #x coord of wolf
self.y = y #y coord of wolf
self.width = width #width of wolf
self.height = height #height of wolf
self.path = [x, finish] # This part here determines the movement limits of the wolf, back and forth
self.run_distance = 0 #wolf is initially did not move
self.velocity = 9 #speed of wolf moving
self.collision_box = (self.x + 60, self.y, 280, 160)#the arguments inside are the coordinates designating the sides of the box
#and then the width and the height of the box
def pathing(self): #pathing system
if self.velocity > 0: # If wolf moving to the right
if self.x < self.path[1] + self.velocity: #ensures that it keeps moving if wolf is not at finish
self.x += self.velocity #allows wolf to move
else: #if the finish is reached then go backwards
self.velocity = self.velocity * -1 #where velocity goes negative
#according to displacement, a particle with -ve velocity goes backwards
self.x += self.velocity #allows wolf to move
else: # If wold is going to the left
if self.x > self.path[0] - self.velocity: #ensures that it keeps moving if the wolf is not at finish
self.x += self.velocity #allows wolf to move
else: #if the finish is reached then go backwards
self.velocity = self.velocity * -1 #where velocity goes negative
#according to displacement, a particle with -ve velocity goes backwards
self.x += self.velocity #allows wolf to move
def got_hit(self): #function if the wolf takes damage from the ninja
print("Congrats, you have hit the wolf!")
def redraw(self, win):#just like for the ninja we do the same steps
self.pathing()
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
if self.velocity < 0: #if velocity is increasing meaning movement, links left images with left movement
win.blit(self.run_left[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
else: #else linking right images with right movement
win.blit(self.run_right[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
#pg.draw.rect(win, (0,200,0), self.collision_box,2) #this will draw a green box around the wolf of lines thickness 2
self.collision_box = (self.x + 50 , self.y, 200, 150) # ensures the box is drawn and is updated alongside motion

The code is not that complicated, but that's my opinion. I read it once through and it was easy to follow. Just one thing, self.x += self.velocity is done at the end of each of the 4 cases in pathing. It is sufficient to do it once at the end of pathing, instead of separately in each case. Something similar can be done for self.run_distance += 1 in redraw:
class wolf(object): #creating the wolf class
# [...]
def pathing(self): #pathing system
if self.velocity > 0 and self.x >= self.path[1] + self.velocity or \
self.velocity < 0 and self.x <= self.path[0] - self.velocity:
self.velocity = self.velocity * -1
self.x += self.velocity
# [...]
def redraw(self, win):#just like for the ninja we do the same steps
self.pathing()
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
run_surf = self.run_left if self.velocity < 0 else self.run_right
win.blit(run_surf[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
#pg.draw.rect(win, (0,200,0), self.collision_box,2) #this will draw a green box around the wolf of lines thickness 2
self.collision_box = (self.x + 50 , self.y, 200, 150) # ensures the box is drawn and is updated alongside motion
Anyway in pygame it is intended to use pygame.Rect, pygame.sprite.Sprite and pygame.sprite.Group.
Each Sprite should have the attributes .rect and .image and the method update(). The Sprites should be contained in Groups. The Groups can be drawn (draw()) and updated (update()).
That makes the code easy to read, short, comprehensible and extendable. e.g.:
(Class Names should normally use the CapWords convention.)
class Wolf(pygame.sprite.Sprite):
wolf_right = ['pics/WR.png'] + ['pics/WR' + str(i) + '.png' for i in range(2, 18)] #identifies the right wolf pics
wolf_left = ['pics/WL.png'] + ['pics/WL' + str(i) + '.png' for i in range(2, 18)] #identifies the left wolf pics
run_right = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_right] #loads the right wolf pics
run_left = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_left] #load the left wolf pics
def __init__(self, x, y, finish):
super().__init__():
self.image = run_left[0]
self.rect = pygame.Rect(x, y, 280, 160)
self.path = [x, finish] # This part here determines the movement limits of the wolf, back and forth
self.run_distance = 0 #wolf is initially did not move
self.velocity = 9 #speed of wolf moving
self.collision_box = (self.rect.x + 60, self.rect.y, 280, 160) #the arguments inside are the coordinates designating the sides of the box
#and then the width and the height of the box
def update(self, angle):
if self.velocity > 0 and self.rect.x >= self.path[1] + self.velocity or \
self.velocity < 0 and self.rect.x <= self.path[0] - self.velocity:
self.velocity = self.velocity * -1
self.rect.x += self.velocity
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
run_surf = self.run_left if self.velocity < 0 else self.run_right
if self.run_distance//3 > len(run_surf):
self.run_distance = 0
self.image = run_surf[self.run_distance//3]
self.run_distance += 1
self.collision_box = (self.rect.x + 50 , self.rect.y, 200, 150) # ensures the box is drawn and is updated alongside motion
wolf = Wolf(........)
all_sprites = pygame.sprite.Group()
all_sprites.add(wolf)
while True:
# [...]
all_sprites.update(win)
# [...]
all_sprites.draw(win)
pygame.display.flip()

Related

How do I implement random direction change on my pygame enemy?

So I currently have an enemy class that moves left, encounters the end of the screen, then flips direction 180 degrees and walks right. It does this constantly.
I wish to make it randomly change direction, so it's more unpredictable. The issue I'm encountering is when I implement a random number generator for the distance of direction, how would I then inherit it into a variable when I create the instance? Or is there a more efficient way to do this?
Entire enemy class:
class enemy(object):#need to use self to access these
walkRight = [pygame.image.load('Game/R1E.png'),pygame.image.load('Game/R2E.png'),pygame.image.load('Game/R3E.png'),pygame.image.load('Game/R4E.png'),pygame.image.load('Game/R5E.png'),pygame.image.load('Game/R6E.png'),pygame.image.load('Game/R7E.png'),pygame.image.load('Game/R8E.png'),pygame.image.load('Game/R9E.png'),pygame.image.load('Game/R10E.png'),pygame.image.load('Game/R11E.png')]
walkLeft = [pygame.image.load('Game/L1E.png'),pygame.image.load('Game/L2E.png'),pygame.image.load('Game/L3E.png'),pygame.image.load('Game/L4E.png'),pygame.image.load('Game/L5E.png'),pygame.image.load('Game/L6E.png'),pygame.image.load('Game/L7E.png'),pygame.image.load('Game/L8E.png'),pygame.image.load('Game/L9E.png'),pygame.image.load('Game/L10E.png'),pygame.image.load('Game/L11E.png')]
def __init__(self,x,y,width,height,end): #get R3E png
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x,self.end]
self.walkCount = 0
self.vel = 3
def draw(self,window): #enemy is gonna move from left, to right, left, to right etc between 2 co ordinate points
self.move()
if self.walkCount + 1 >= 33: #if sign is changed, as walkcount would always be less than 33, it wouldn't change
self.walkCount = 0
#rather than using self.left, sel.right, we can use out velocity
if self.vel > 0: #this means we're moving right, integer division 3 so it doesn't look like we going too fast
window.blit(self.walkRight[self.walkCount //3],(self.x,self.y))
self.walkCount += 1
else:
window.blit(self.walkLeft[self.walkCount //3],(self.x,self.y))
self.walkCount += 1 #if not moving right, we're moving left
#check if we're drawing an image to the left or right
def move(self): #move method #to change directions, he needs to change velocity (multiply by -1 etc)
if self.vel > 0:
if self.x < self.path[1] + self.vel:#check if he's about to move past the point on screen, we're accessing the self.end eleme
self.x += self.vel
else: #e.g. if it's greater than, we change direction
self.vel = self.vel * -1 #flipped 180 degrees, so goes other direction
self.x += self.vel
self.walkCount = 0
else: #if our vel is negative
if self.x > self.path[0] - self.vel:
self.x += self.vel #vel is gonna be negative already if we've changed directions
else:
self.vel = self.vel * -1
self.x += self.vel
self.walkCount = 0
Movement function:
def move(self): #move method #to change directions, he needs to change velocity (multiply by -1 etc)
if self.vel > 0:
if self.x < self.path[1] + self.vel:#check if he's about to move past the point on screen, we're accessing the self.end eleme
self.x += self.vel
else: #e.g. if it's greater than, we change direction
self.vel = self.vel * -1 #flipped 180 degrees, so goes other direction
self.x += self.vel
self.walkCount = 0
else: #if our vel is negative
if self.x > self.path[0] - self.vel:
self.x += self.vel #vel is gonna be negative already if we've changed directions
else:
self.vel = self.vel * -1
self.x += self.vel
self.walkCount = 0
Creating the instance just before the main loop(x,y,height,width,walk distance):
goblin = enemy(0,440,64,64,450)
My attempt - still only takes one random number & permanently uses it whilst the game runs.
def timerthing():
pathenemy = random.randint(0,450)
return pathenemy
#now to write main loop which checks for collisions, mouse events etc
#make path randomly change
man = player(200,444,64,66) #creates instance of the player / object
goblin = enemy(0,440,64,64,timerthing())#need to create instance of enemy so he appears on the screen, path ends at timerthing
run = True
bullets = []
while run:
clock.tick(15) #frame rate
timerthing()
Instead of a constant of 450, I'm after a constantly changing variable instead, so his movement is unpredictable. Any other suggestions would be appreciated - thanks.
import random
def timerthing():
pathenemy = random.randint(0,450)
time.sleep(1)
return pathenemy
But I suspect that's not really your problem. It sounds like you have a configuration problem rather than a source problem, and to solve that we'd need an entirely different kind of information from you.
If I had to do code this randomly moving enemy, I would implement two methods first:
One method that returns the possible directions my enemy can go through, given the state of the board. Yet I can't help you with that, since we don't have the "board" part of your code. Let's call this method getPossibleDirections()
One method move, with one argument direction, representing the direction taken by my enemy. Let's call this method move(direction)
Based on your code, I guess you can implement these two methods by yourself. And maybe you want your ennemy to move in the y direction too?
Like #Mark Storer said, you could use the random package, but I will use the random part of the numpy library (already included in python) for my answer:
# you will need to import numpy in the beginning of your file
import numpy as np
class enemy(...)
# your code goes there
def getPossibleDirections(self):
#your implementation
pass
def move(self, direction):
#your implementation
pass
def moveRandomly(self):
possibleDirections = self.getPossibleDirections()
chosenDirection = np.random.randint(0,len(possibleDirections))
# returns one number between 0 and len(possibleDirections) (not included) that can represent your direction
self.move(possibleDirection[chosenDirection])
To move your ennemy randomly, you will just have to call enemy.moveRandomly().

Generate enemies around the player from all directions randomly

The enemy are being generated from above the screen and then move toward player in the middle, I want to generate enemies randomly around the screen from all directions but not inside the screen directly and proceed to move towards the player and also enemy sprites are sometimes joining combining and moving together how to repel the enemy sprites.
I have tried changing x,y coordinates of enemy objects using a random range but sometimes they generate objects inside the play screen, I want enemies to generate outside the playing window.
class Mob(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('enemy.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('enemy.png'), (33, 33))
self.image_orig = self.image.copy()
self.radius = int(29 * .80 / 2)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = 4
self.rot = 0
self.rot_speed = 5
self.last_update = pg.time.get_ticks()
def rotate(self):
now = pg.time.get_ticks()
if now - self.last_update > 50:
self.last_update = now
self.rot = (self.rot + self.rot_speed) % 360
new_image = pg.transform.rotozoom(self.image_orig, self.rot, 1)
old_center = self.rect.center
self.image = new_image
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
self.rotate()
dirvect = pg.math.Vector2(rotator.rect.x - self.rect.x,
rotator.rect.y- self.rect.y)
if dirvect.length_squared() > 0:
dirvect = dirvect.normalize()
# Move along this normalized vector towards the player at current speed.
if dirvect.length_squared() > 0:
dirvect.scale_to_length(self.speed)
self.rect.move_ip(dirvect)
if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 20:
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1, 4)
[UPDATE]
This the remaining code:
import math
import random
import os
import pygame as pg
import sys
pg.init()
height = 650
width = 1200
os_x = 100
os_y = 45
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (os_x, os_y)
screen = pg.display.set_mode((width, height), pg.NOFRAME)
screen_rect = screen.get_rect()
background = pg.image.load('background.png').convert()
background = pg.transform.smoothscale(pg.image.load('background.png'), (width, height))
clock = pg.time.Clock()
running = True
font_name = pg.font.match_font('Bahnschrift', bold=True)
def draw_text(surf, text, size, x, y, color):
[...]
class Mob(pg.sprite.Sprite):
[...]
class Rotator(pg.sprite.Sprite):
def __init__(self, screen_rect):
pg.sprite.Sprite.__init__(self)
self.screen_rect = screen_rect
self.master_image = pg.image.load('spaceship.png').convert_alpha()
self.master_image = pg.transform.smoothscale(pg.image.load('spaceship.png'), (33, 33))
self.radius = 12
self.image = self.master_image.copy()
self.rect = self.image.get_rect(center=[width / 2, height / 2])
self.delay = 10
self.timer = 0.0
self.angle = 0
self.distance = 0
self.angle_offset = 0
def get_angle(self):
mouse = pg.mouse.get_pos()
offset = (self.rect.centerx - mouse[0], self.rect.centery - mouse[1])
self.angle = math.degrees(math.atan2(*offset)) - self.angle_offset
old_center = self.rect.center
self.image = pg.transform.rotozoom(self.master_image, self.angle, 1)
self.rect = self.image.get_rect(center=old_center)
self.distance = math.sqrt((offset[0] * offset[0]) + (offset[1] * offset[1]))
def update(self):
self.get_angle()
self.display = 'angle:{:.2f} distance:{:.2f}'.format(self.angle, self.distance)
self.dx = 1
self.dy = 1
self.rect.clamp_ip(self.screen_rect)
def draw(self, surf):
surf.blit(self.image, self.rect)
def shoot(self, mousepos):
dx = mousepos[0] - self.rect.centerx
dy = mousepos[1] - self.rect.centery
if abs(dx) > 0 or abs(dy) > 0:
bullet = Bullet(self.rect.centerx, self.rect.centery, dx, dy)
all_sprites.add(bullet)
bullets.add(bullet)
There's not much informations to go by here, but you probably need to check the x and y range your play window has and make sure the random spawn coordinates you generate are outside of it:
In your init:
# These are just example min/max values. Maybe pass these as arguments to your __init__ method.
min_x = min_y = -1000
max_x = max_y = 1000
min_playwindow_x = min_playwindow_y = 500
max_playwindow_x = max_playwindow_y = 600
self.x = (random.randrange(min_x, min_playwindow_x), random.randrange(max_playwindow_x, max_x))[random.randrange(0,2)]
self.y = (random.randrange(min_y, min_playwindow_y), random.randrange(max_playwindow_y, max_y))[random.randrange(0,2)]
This solution should work in basically any setup. For x and y it generates a tuple of values outside the playing window. Then a coinflip decides on the value. This will only spawn mobs that are diagonally outside the playing field, but it will always generate valid random coordinates.
Another approach would be just generating as many random variables as needed to get a valid pair like this:
while min_playingwindow_x <= self.x <= max_playingwindow_x and
min_playingwindow_y <= self.y <= max_playingwindow_y:
# While within screen(undesired) calculate new random positions
self.x = random.randrange(min_x, max_x)
self.y = random.randrange(min_y, max_y)
This can be really slow however if your valid amount of positions is (for example) only 1% of the total positions.
IF you need something really fleshed out, you need to know the corners of both your map and the rectangle that is actually displayed, which is I assume smaller than the entire map(otherwise you cannot spawn enemies outside your view.
(0,0)
+----------------------+
| A |
|-----+-----------+----|
| D | W | B |
|-----+-----------+----|
| C |
+----------------------+(max_x, max_y)
In this diagram W is the window that is acutally visible to the player, and A,B,C,D together are the part of your map that is not currently visible. Since you only want to spawn mobs outside the player's view, you'll need to make sure that the coordinates you generate are inside your map and outside your view:
def generate_coordinates_outside_of_view(map_width=1000, map_height=1000, view_window_top_left=(100, 100),
view_width=600, view_height=400):
"""
A very over the top way to generate coordinates outside surrounding a rectangle within a map almost without bias
:param map_width: width of map in pixels (note that 0,0 on the map is top left)
:param map_height: height of map in pixels
:param view_window_top_left: top left point(2-tuple of ints) of visible part of map
:param view_width: width of view in pixels
:param view_height: height of view in pixels
"""
from random import randrange
# generate 2 samples for each x and y, one guaranteed to be random, and one outside the view for sure.
x = (randrange(0, map_width), (randrange(0, view_window_top_left[0]),
randrange(view_window_top_left[0] + view_width, map_width))[randrange(0, 2)])
y = (randrange(0, map_height), (randrange(0, view_window_top_left[1]),
randrange(view_window_top_left[1] + view_height, map_height))[randrange(0, 2)])
# now we have 4 values. To get a point outside our view we have to return a point where at least 1 of the
# values x/y is guaranteed to be outside the view.
if randrange(0, 2) == 1: # to be almost completely unbiased we randomize the check
selection_x = randrange(0, 2)
selection_y = randrange(0, 2) if selection_x == 1 else 1
else:
selection_y = randrange(0, 2)
selection_x = randrange(0, 2) if selection_y == 1 else 1
return x[selection_x], y[selection_y]
HTH

Making an opponent object using a class not working properly; they don't display on the screen for some reason?

So a while ago I learned how to create a class for which had the purpose of creating an "opponent" which would basically fight the player and etc. It was a good tutorial and while I did learn how to create this type of class, I also got issues from the code itself when testing it out.
One of the reasons it didn't work properly was because of flipping my sprites horizontally; I have 2 sprite variables, 1 that loads the images and the other which is supposed to contain a list. A loop then "flips" all the images from the original and stores it inside the empty list. This, however, caused an issue and started to make my sprite "flash" on the screen in both directions so I removed the loop, tried again and it worked but this time it only had one sprite(facing the left).
I also tried to remove the variables and the loop outside the class but that ended up not displaying the image at all.
#Goku Black
walkLeftGB = [ pygame.image.load("GB1.png"), pygame.image.load("GB2.png"), pygame.image.load("GB3.png"), pygame.image.load("GB4.png") ]
walkRightGB = []
for l in walkLeftGB:
walkRightGB.append(pygame.transform.flip(l, True, False))
for x in range(len(walkLeftGB)):
walkLeftGB[x] = pygame.transform.smoothscale(walkLeftGB[x], (372, 493))
for x in range(len(walkRightGB)):
walkRightGB[x] = pygame.transform.smoothscale(walkRightGB[x], (372, 493))
# === CLASSES === (CamelCase names)
class Enemy(object):
global vel
global walkCount1
global walkRightGB, walkLeftGB
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.end]
self.walkCount1 = 0
self.vel = 3
self.walkLeftGB = walkLeftGB
self.walkRightGB = walkRightGB
def draw(self, DS):
self.move()
global walkCount1
if self.walkCount1 + 1 <= 33:
self.walkCount1 = 0
if self.vel > 0:
DS.blit(self.walkRightGB[self.walkCount1 //4], (self.x, self.y))
self.walkCount1 += 1
else:
DS.blit(self.walkLeftGB[self.walkCount1 //4], (self.x, self.y))
self.walkCount1 += 1
def move(self):
global walkCount1
if self.vel > 0:
if self.x + self.vel < self.path[1]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.walkCount1 = 0
else:
if self.x - self.vel > self.path[0]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.walkCount1 = 0
man = Enemy(1600, 407, 96, 136, 22)
def redrawGameWindow():
global walkCount
pygame.display.update()
man.draw(DS)
DS.blit(canyon,(0,0))
lastMoved = "left"
if walkCount + 1 >= 27:
walkCount = 0
if left:
DS.blit(walkLeft[walkCount//3],(x,y))
walkCount +=1
lastMoved = "left"
elif right:
DS.blit(walkRight[walkCount//3], (x,y))
walkCount +=1
lastMoved = "right"
else: #this is when its moving neither left or right
if lastMoved == "left":
DS.blit(char2, (x, y))
else:
DS.blit(char, (x, y))
#The "redrawGameWindow" is then called in a loop inside a function.
After I made those changes mentioned above, the output received was now the image not appearing at all, I expected the output for the image to appear(and maybe move)
This does not appear to do what you want it to:
if self.walkCount1 + 1 <= 33:
self.walkCount1 = 0
As a detail, it's weird to add one and compare to 33, when if self.walkCount1 <= 32 would suffice.
More importantly, it looks like you wanted >= there.
You have some hard-coded magic number divisors: // 3 and // 4.
Rather than e.g. 4, it would be much better to
refer to len(self.walkRightGB).
Then you could choose to insert new interpolated walking images,
or delete some, without having to worry about correctness of
other code that may be affected.
Numbers like 27 should also be expressed in more meaningful terms.
It's not clear to me you want // integer division.
Possibly you were looking for % modulo instead.
As written, it looks like there's a danger of the code
trying to access past the end of an image array.
Printing out the counts would help you to debug this,
for example by verifying that incremented count is preserved
across function calls.

Sprite not moving in the same direction it is facing

I have an issue with my sprite not moving in the same direction as it is facing. It is top view of a beetle, 10 images animating its leg movements. The sprite animation works fine, choosing new 'random' direction by rotating the image and re-centering to previous center is working too.
What I can't get to work is the sprite to move 'forward', that is to move in the new direction it chooses/faces every second or so. The new direction is simply a small 10-15 degrees rotation left or right from previous position. Instead it moves in what seem like random movements every time the sprite chooses a new direction. For example it will move southwest while facing east, or move north while facing south etc.
I suspect the problem is in the move() method where the movement isn't properly translated via trig values. I am adding to the rect.x values because it is the same as the cartesian coords system while subtracting from rect.y values because it is inverted in pygame compared to cartesian. Going down is increasing positively, up - decreasing y values, increasing negatively.
The class for the beetle sprite is below:
class Foe():
def __init__(self, location):
self.sprites = []
for i in range(1, 11):
file = pg.image.load("beetleprac1/000"+str(i)+"a.png").convert() # 10 sprite files named "0001a-10a.png" last 1 is idle stance
#file.set_colorkey((0, 255, 255))
self.sprites.append(file)
self.move_anim_index = 9
self.image = self.sprites[self.move_anim_index]
self.rotated_image = self.image.copy()
self.rect = self.image.get_rect(center=location)
self.move_rect_coords = [self.rect.x, self.rect.y]
self.angle = 90
self.speed = 3
self.time = 0
self.frames_counter = 0
def move_anim(self, rate=2): # rate is in frames
if self.frames_counter % rate == 0:
self.move_anim_index += 1
if self.move_anim_index >= 9:
self.move_anim_index = 0
self.image = self.sprites[self.move_anim_index]
#self.rect = self.image.get_rect(center=self.rect.center)
self.rotated_image = self.image.copy()
self.rotated_image = pg.transform.rotate(self.image, self.angle - 90)
self.rect = self.rotated_image.get_rect(center=self.rect.center)
def move(self, last_tick):
self.move_anim()
self.rect.x += int(self.speed * math.cos(self.angle))
self.rect.y -= int(self.speed * math.sin(self.angle))
self.move_rect_coords = [self.rect.x, self.rect.y]
def change_direction(self, change_time): # change_time in frames
if self.frames_counter % change_time == 0:
a = random.choice([-1, 1])
self.angle += 15
if self.angle < 0: self.angle += 360
if self.angle > 360: self.angle -=360
#if self.rect.x <
def track_time(self, last_tick, amount):
self.time += last_tick
self.frames_counter += 1
if self.time >= last_tick * amount:
self.time = 0
self.counter = 0
def think(self, last_tick):
self.track_time(last_tick, FPS*2)
self.change_direction(FPS)
def update(self, last_tick, screen_rect):
self.think(last_tick)
self.move(last_tick)
def draw(self, screen):
screen.blit(self.rotated_image, self.move_rect_coords)
And here is the full code and repository at github: https://github.com/fn88/buganimprac2
sin(), cos() and other trigonometric functions use radians -> cos(math.radians(angle))

pygame bullet physics messed up by scrolling

the code below is the bullet class for my shooter game in pygame. as you can see if you run the full game (https://github.com/hailfire006/economy_game/blob/master/shooter_game.py) the code works great to fire bullets at the cursor as long as the player isn't moving. However, I recently added scrolling, where I change a global offsetx and offsety every time the player gets close to an edge. These offsets are then used to draw each object in their respective draw functions.
unfortunately, my bullet physics in the bullet's init function no longer work as soon as the player scrolls and the offsets are added. Why are the offsets messing up my math and how can I change the code to get the bullets to fire in the right direction?
class Bullet:
def __init__(self,mouse,player):
self.exists = True
centerx = (player.x + player.width/2)
centery = (player.y + player.height/2)
self.x = centerx
self.y = centery
self.launch_point = (self.x,self.y)
self.width = 20
self.height = 20
self.name = "bullet"
self.speed = 5
self.rect = None
self.mouse = mouse
self.dx,self.dy = self.mouse
distance = [self.dx - self.x, self.dy - self.y]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
direction = [distance[0] / norm, distance[1] / norm]
self.bullet_vector = [direction[0] * self.speed, direction[1] * self.speed]
def move(self):
self.x += self.bullet_vector[0]
self.y += self.bullet_vector[1]
def draw(self):
make_bullet_trail(self,self.launch_point)
self.rect = pygame.Rect((self.x + offsetx,self.y + offsety),(self.width,self.height))
pygame.draw.rect(screen,(255,0,40),self.rect)
You don't take the offset into account when calculating the angle between the player and the mouse. You can fix this by changing the distance like this:
distance = [self.dx - self.x - offsetx, self.dy - self.y - offsety]

Categories