'pygame.Surface' object has no attribute 'update' - python

I get the message : 'pygame.Surface' object has no attribute 'update'. But as you can see, i have an update function in the code. wha did i wrong? I looked around but i didn't fina a simular question.
class Createparticle:
def __init__(self, xx, yy,img):
self.x = xx
self.y = yy
self.img = img
self.particlelist = []
self.verzoegerung = 0
self.scale_k = 0.1
self.img = scale(img, self.scale_k)
self.alpha = 255
self.alpha_rate = 3
self.alive = True
self.vx = 0
self.vy = 4 + random.randint(-10, 10) / 10
self.k = 0.01 * random.random() * random.choice([-1, 1])
def update(self):
self.x += self.vx
self.vx += self.k
self.y -= self.vy
self.vy *= 0.99
self.scale_k += 0.005
self.alpha -= self.alpha_rate
self.img = scale(self.img, self.scale_k)
self.img.set_alpha(self.alpha)
self.particlelist = [i for i in self.particlelist if i.alive]
self.verzoegerung += 1
if self.verzoegerung % 2 == 0:
self.verzoegerung = 0
self.particlelist.append(self.img)
for i in self.particlelist:
i.update()
def draw(self):
for i in self.particlelist:
screen.blit(self.img, self.img.get_rect(center=(self.x, self.y)))
createparticle = Createparticle(500,300,basisbild)
while True:
screen.fill((0, 0, 0))
createparticle.update()
createparticle.draw()
pygame.display.update()
clock.tick(FPS)

The error is caused by i.update(). i is an element from self.particlelist. In your case self.particlelist is an image (pygame.Surface). A pygame.Surface object has no update method. Probably i should not be a pygame.Surface, but you add pygame.Surface objects to the list:
self.particlelist.append(self.img)
So this line of code is obviously wrong and should be like this instead (note: Particle is a guess of mine, but I don't know how you named your classes.):
self.particlelist.append(Particle(self.img))

you have a list of surfaces called particlelist, in your update function you are calling the update function on each item in that list. Since theses are of the 'surface' type they don't have a update function. This is where the error is coming from.

Related

'list' object has no attribute 'get_rect' Error How To Fix?

so I have this script here and I am getting this error 'list' object has no attribute 'get_rect' I am not sure how to fix this
error 'list' object has no attribute 'get_rect'
what this does is that it moves my player image into my square hitbox
def draw(self):
self.topleft = (self.x,self.y)
player_rect = rights.get_rect(center = self.rect.center)
player_rect.centerx += 0 # 10 is just an example
player_rect.centery += 0 # 15 is just an example
and this is my player class where I stored the code down below
# my players class
class player:
def __init__(self,x,y,height,width,color):
self.x = x
self.y = y
self.height = height
self.width = width
self.color = color
self.isJump = False
self.JumpCount = 10
self.fall = 0
self.speed = 5
self.standingleft =[pygame.image.load("s1.png"),
pygame.image.load("s2.png"),
pygame.image.load("s3.png"),
pygame.image.load("s4.png"),
pygame.image.load("s5.png"),
pygame.image.load("s6.png"),
pygame.image.load("s7.png"),
pygame.image.load("s8.png"),
pygame.image.load("s9.png"),
pygame.image.load("s10.png"),
pygame.image.load("s11.png"),
pygame.image.load("s12.png"),
pygame.image.load("s13.png"),
pygame.image.load("s14.png"),
pygame.image.load("s15.png")]
self.standingright = [pygame.image.load("d1.png"),
pygame.image.load("d2.png"),
pygame.image.load("d3.png"),
pygame.image.load("d4.png"),
pygame.image.load("d5.png"),
pygame.image.load("d6.png"),
pygame.image.load("d7.png"),
pygame.image.load("d8.png"),
pygame.image.load("d9.png"),
pygame.image.load("d10.png"),
pygame.image.load("d11.png"),
pygame.image.load("d12.png"),
pygame.image.load("d13.png"),
pygame.image.load("d14.png"),
pygame.image.load("d15.png")]
self.rights = [pygame.image.load("L1.png"),
pygame.image.load("L2.png"),
pygame.image.load("L3.png"),
pygame.image.load("L4.png"),
pygame.image.load("L5.png"),
pygame.image.load("L6.png"),
pygame.image.load("L7.png"),
pygame.image.load("L8.png"),
pygame.image.load("L9.png"),
pygame.image.load("L10.png"),
pygame.image.load("L11.png"),
pygame.image.load("L12.png"),
pygame.image.load("L13.png"),
pygame.image.load("L14.png"),
pygame.image.load("L15.png")]
self.lefts = [pygame.image.load("e1.png"),
pygame.image.load("e2.png"),
pygame.image.load("e3.png"),
pygame.image.load("e4.png"),
pygame.image.load("e5.png"),
pygame.image.load("e6.png"),
pygame.image.load("e7.png"),
pygame.image.load("e8.png"),
pygame.image.load("e9.png"),
pygame.image.load("e10.png"),
pygame.image.load("e11.png"),
pygame.image.load("e12.png"),
pygame.image.load("e13.png"),
pygame.image.load("e14.png"),
pygame.image.load("e15.png")]
self.left_index = 0
self.right_index = 0
self.stand_index = 0
self.anim_index = 0
self.direction = "left"
self.direction = "right"
self.direction = "standright"
self.rights = [pygame.transform.scale(image,(image.get_width()-400,image.get_height()-400)) for image in self.rights]
self.lefts = [pygame.transform.scale(image,(image.get_width()-10,image.get_height()-10)) for image in self.lefts]
self.standingright = [pygame.transform.scale(image,(image.get_width()-10,image.get_height()-10)) for image in self.standingright]
self.standingleft = [pygame.transform.scale(image,(image.get_width()-10,image.get_height()-10)) for image in self.standingleft]
self.rect = pygame.Rect(x,y,height,width)
def draw(self):
self.topleft = (self.x,self.y)
player_rect = rights.get_rect(center = self.rect.center)
player_rect.centerx += 0 # 10 is just an example
player_rect.centery += 0 # 15 is just an example
window.blit(heart, player_rect)
if self.direction == "right":
window.blit(self.right[self.anim_index],self.rect)
self.anim_index += 1
if self.anim_index == len(self.right):
self.anim_index = 0
player classplayer classplayer classplayer classplayer classplayer classplayer class
All the images are scaled to the same size by
pygame.transform.scale(image,(image.get_width()-400,image.get_height()-400))
Hence you can get the size from one representative surface object. Use the 1st surface in the list to get the size (rights[0]):
player_rect = rights.get_rect(center = self.rect.center)
player_rect = right[0].get_rect(center = self.rect.center)
You do not indicate which line the error was on, but I can guess it is this line.
player_rect = rights.get_rect(center = self.rect.center)
The error is relatively explanatory. "'list' object has no attribute 'get_rect'". says that tried to call get_rect() on a list, not a object like a surface that has a get_rect().
rights a list of images/surfaces and you are calling get_rect() on that list rather that on one of the things in the list.

Is there a way to move an object in pygame in random directions smoothly? [duplicate]

This question already has an answer here:
Pygame game help: Easing/Acceleration
(1 answer)
Closed 2 years ago.
I'm trying to make blobs move in a random direction for several frames rather than just once so that it appears less jerky and more smooth, but have been unable to do so. Is there any way to make each object move in the same direction for several ticks before choosing another random direction and doing the same?
My code (most is irrelevant):
import pygame
import random
import numpy as np
WIDTH = 1800
HEIGHT = 1000
BLUE = (15,15,180)
RED = (150,0,0)
class Blob:
def __init__(self, colour, x_boundary, y_boundary, size):
self.colour = colour
self.size = size
self.x_boundary = x_boundary
self.y_boundary = y_boundary
self.x = random.randrange(0, self.x_boundary)
self.y = random.randrange(0, self.y_boundary)
def move(self):
self.x += random.randrange(-6,7)
self.y += random.randrange(-6,7)
def limits(self):
if self.x < 0:
self.x = 0
elif self.x > self.x_boundary:
self.x = self.x_boundary
if self.y < 0:
self.y = 0
elif self.y > self.y_boundary:
self.y = self.y_boundary
def __add__(self, other_blob):
if other_blob.size > self.size:
other_blob.size += int(self.size * 0.5)
self.size = 0
class FastBlob(Blob):
def __init__(self, colour, x_boundary, y_boundary, size):
super().__init__(colour, x_boundary, y_boundary, size)
def move(self):
self.x += random.randrange(-20,21)
self.y += random.randrange(-20,21)
pygame.init()
game_display = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Blob world')
clock = pygame.time.Clock()
def is_touching(b1,b2):
return np.linalg.norm(np.array([b1.x,b1.y])-np.array([b2.x,b2.y])) < (b1.size + b2.size)
def handle_collisions(blob_list):
blues, reds, slow_reds = blob_list
for first_blobs in blues, reds, slow_reds:
for first_blob_id, first_blob in first_blobs.copy().items():
for other_blobs in blues, reds, slow_reds:
for other_blob_id, other_blob in other_blobs.copy().items():
if first_blob == other_blob:
pass
else:
if is_touching(first_blob, other_blob):
first_blob + other_blob
return blues, reds, slow_reds
def draw_environment(blob_list):
game_display.fill((210,210,210))
handle_collisions(blob_list)
for blob_dict in blob_list:
for blob_id in blob_dict:
blob = blob_dict[blob_id]
pygame.draw.circle(game_display, blob.colour, [blob.x, blob.y], blob.size)
blob.move()
blob.limits()
pygame.display.update()
def main():
blue_blobs = dict(enumerate([FastBlob(BLUE, WIDTH, HEIGHT, random.randrange(10,15)) for i in range(20)]))
red_blobs = dict(enumerate([FastBlob(RED, WIDTH, HEIGHT, random.randrange(5,10)) for i in range(30)]))
slow_red_blobs = dict(enumerate([Blob(RED, WIDTH, HEIGHT, random.randrange(20,30)) for i in range(5)]))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
draw_environment([blue_blobs, red_blobs, slow_red_blobs])
clock.tick(7)
if __name__ == '__main__':
main()
Here, I have similar problem in my game, when enemy has to randomly change directions so it is unpredictable to the player.
def goblin_move(): #Goblin auto (random) movement
if goblin.x < 0:
goblin.go_right()
elif goblin.x > 500:
goblin.go_left()
else:
if goblin.x > (rnd_1 * win_width) and goblin.move_flag == -1:
goblin.go_left()
goblin.move_flag = -1
else:
goblin.go_right()
goblin.move_flag = 1
if goblin.x > (rnd_2 * win_width):
goblin.move_flag = -1
def set_random(rnd_1, rnd_2): #Random function generator
rnd_1 = round(random.uniform(0, 0.45), 2)
rnd_2 = round(random.uniform(0.65, 0.95), 2)
return rnd_1, rnd_2
And this is how I set it in the main loop:
if round(pg.time.get_ticks()/1000) % 3 == 0: #Calling random function
(rnd_1, rnd_2) = set_random(rnd_1, rnd_2)
Hope you will find it useful.
Use pygame.math.Vector2 to do the computations. Store the coordinates of the blob to a Vector2 and define a maximum distance (self.maxdist), a velocity (self.speed), a random distance (self.dist) a nd a random direction (self.dir). The random direction is a vector with length 1 (Unit vector) and a random angel (rotate()):
class Blob:
def __init__(self, colour, x_boundary, y_boundary, size):
self.colour = colour
self.size = size
self.x_boundary = x_boundary
self.y_boundary = y_boundary
self.x = random.randrange(0, self.x_boundary)
self.y = random.randrange(0, self.y_boundary)
self.pos = pygame.math.Vector2(self.x, self.y)
self.maxdist = 7
self.speed = 1
self.dist = random.randrange(self.maxdist)
self.dir = pygame.math.Vector2(1, 0).rotate(random.randrange(360))
When the blob moves, then scale the direction by the speed and add it to the position (self.pos += self.dir * self.speed). Decrement the distance (self.dist -= self.speed) and update self.x, self.y by the rounded (round) position. If self.dist falls below 0, the create a new random direction and distance:
class Blob:
# [...]
def move(self):
self.pos += self.dir * self.speed
self.dist -= self.speed
self.x, self.y = round(self.pos.x), round(self.pos.y)
if self.dist <= 0:
self.dist = random.randrange(self.maxdist)
self.dir = pygame.math.Vector2(1, 0).rotate(random.randrange(360))
In the method limit you have to ensure that self.pos is in bounds. Finally you have to update self.x, self.y:
class Blob:
# [...]
def limits(self):
if self.pos.x < 0:
self.pos.x = 0
elif self.pos.x > self.x_boundary:
self.pos.x = self.x_boundary
if self.pos.y < 0:
self.pos.y = 0
elif self.pos.y > self.y_boundary:
self.pos.y = self.y_boundary
self.x, self.y = round(self.pos.x), round(self.pos.y)
The class FastBlob does not need its own move method. It is sufficient do define its own self.maxdist and self.speed:
class FastBlob(Blob):
def __init__(self, colour, x_boundary, y_boundary, size):
super().__init__(colour, x_boundary, y_boundary, size)
self.maxdist = 35
self.speed = 5

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.

Lennard Jones interaction between particles. Particles moving to one point

import numpy as np
import random
import pygame
background_colour = (255,255,255)
width, height = 300, 325
eps = 1
sigma = 1
dt = 0.05
class Particle():
def __init__(self):
self.x = random.uniform(0,400)
self.y = random.uniform(0,500)
self.vx = random.uniform(-.1,.1)
self.vy = random.uniform(-.1,.1)
self.fx = 0
self.fy = 0
self.m = 1
self.size = 10
self.colour = (0, 0, 255)
self.thickness = 0
def bounce(self):
if self.x > width - self.size:
self.x = 2*(width - self.size) - self.x
elif self.x < self.size:
self.x = 2*self.size - self.x
if self.y > height - self.size:
self.y = 2*(height - self.size) - self.y
elif self.y < self.size:
self.y = 2*self.size - self.y
def getForce(self, p2):
dx = self.x - p2.x
dy = self.y - p2.y
self.fx = 500*(-8*eps*((3*sigma**6*dx/(dx**2+dy**2)**4 - 6*sigma**12*dx/(dx**2+dy**2)**7)))
self.fy = 500*(-8*eps*((3*sigma**6*dy/(dx**2+dy**2)**4 - 6*sigma**12*dy/(dx**2+dy**2)**7)))
return self.fx, self.fy
def verletUpdate(self,dt):
self.x = self.x + dt*self.vx+0.5*dt**2*self.fx/self.m
self.y = self.y + dt*self.vy+0.5*dt**2*self.fy/self.m
def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
partList = []
for k in range(10):
partList.append(Particle())
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(background_colour)
for k, particle in enumerate(partList):
for p2 in partList[k+1:]:
particle.getForce(p2)
particle.verletUpdate(dt)
particle.bounce()
particle.display()
pygame.display.flip()
pygame.quit()
Is my code correct? I tried to simulate particles in 2D move with Lennard Jones forces. I think calculating forces works okay but why my particles are moving to one point? Ocasionally I also get error OverflowError: Python int too large to convert to C long Any advice would be useful.
I can not comment on the physics of the simulation, but as far as the display is concerned following are my observations:
Your particles move to one point because the update condition for the x and y parameter in your code in verletUpdate are slowly moving to values beyond the display area. Also to values out of the range of the int() function which is causing your error. You can see this with the statement:
def verletUpdate(self,dt):
self.x = self.x + dt*self.vx+0.5*dt**2*self.fx/self.m
self.y = self.y + dt*self.vy+0.5*dt**2*self.fy/self.m
print self.x
print self.y
Sample Output:
290.034892392
9.98686293664
290.028208837
9.99352484332
-2.55451579742e+19
1.12437640586e+19
Also they saturate and with iterations, the update gets smaller and smaller:
def display(self):
print ' %s + %s '%(self.x,self.y)
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
Output:
10.0009120033 + 10.0042647307
10.0009163718 + 10.0000322065
10.0009120033 + 10.0042647307
10.0009163718 + 10.0000322065
...
10.0009163718 + 10.0000322065
10.0009120033 + 10.0042647307
10.0009163718 + 10.0000322065
This is also why your bounce functions and your limit checking is not working. And after a lot of iterations on occasion your self.x and self.y are far exceeding the limits of int().
The code seems fine, but you can get rid of the overflow error by adding some checks above the draw line. For instance I initialized them randomly again to simulate a particle going off screen and us tracking a new one. Feel free to change it.
def display(self):
if(self.x<0 or self.x>height):
self.__init__()
print "reset"
if(self.y<0 or self.y>width):
self.__init__()
print "reset"
print ' %s + %s '%(self.x,self.y)
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
Also at one point you adress the array as [k+1:], and addressing the zero element caused a divide by zero error. You might want to look at that.

Using pygame.Surface.blit inside class

I can call my class only one time.
My code is:
class fish:
def __init__(self, x, y, image, speed):
self.x = x
self.y = y
self.image = image
self.speed = speed
def be(self):
screen.blit(self.image, (self.x, self.y))
self.x -= self.speed
if boot.x+36 > self.x and boot.x < self.x+5:
if boot.y+34 > self.y and boot.y < self.y+5:
boot.live -= 1
boot.x = 100
boot.y = 460
fishes = []
fishes.append(fish(900, 300, fish_1, 1))
And when I call 'fish' object inside or outside the game loop(fishes.append(fish(900, 300, fish_1, 1))
I got Error:
TypeError: 'fish' object is not callable
My first guess is that you have a fish variable somewhere down in your code. Rename the class to Fish as well as the constructor call to Fish(900, 300, fish_1, 1) and it should be fine.

Categories