Create a lot of objects and delete them - python

so i'm kinda new to programming, but for now with python and pygame i'm trying to create a small game. It is quite simple, the player will move around dodging small projectiles. However i'm having trouble creating a lot of objects, for example i need to randomly generate a position and a speed for a new projectile, and how do i create many objects like that, plus when they actually go 'out of the screen' they should disapear.
So I also need to delete them. My first idea was to create a list of objects, I and a loop that would move them one by one before updating the screen, but how do i create new objects with different names while I don't know how many projectiles will be on the screen, it should be random.
class Projectiles:
ProjectilesCount = 0
def __init__(self, x, y, speed):
self.pos = (x,y)
self.speed = speed
Projectiles.ProjectilesCount += 1
def moveProj(self):
x, y = self.pos
x -= self.speed
self.pos = (x,y)
pygame.draw.line(DISPLAY, WHITE, self.pos, (x,y+self.SIZE), self.SIZE)
bullet = Projectiles(500,200,5)
bullet.SIZE = SIZE
while true:
# some stuff
bullet.moveProj()
pygame.display.update()
fpsClock.tick(FPS)
This is the class i use for now (it just goes left) and it works for just one projectile.

You want a list:
list_of_bullets = []
for i in range(100):
list_of_bullets.append(Projectiles(...))

Use dictionaries. A list will work, but when using objects where the order is not important use dictionaries. Dictionaries also perform better when iterating through lots of data within them. Use some code similar to below...
bulletdict = {}
#This is a list because we can change the value within a function without returning it
#You could also use a global variable instead of a list
bulletcount = [0]
def SpawnBullet(bulletdict,bulletcount):
bulletdict[bulletcount] = Projectiles
bulletcount[0] += 1
The key will be a number and the value will be the bullet itself. To iterate through the dictionary to update stuff do...
for item in bulletdict:
bulletdict[item].moveproj(...)
If you need to remove stuff use code similar to this. I usually have 'checkDelete' functions inside my objects that I can call to check to see if we should remove them. This is used for things like deleting objects if they get off the screen or collide with something.
def deleteObjects(objectdict):
deletelist = []
for item in objectdict:
if objectdict[item].checkdelete() == True:
deletelist.append(item)
for item in deletelist:
del objectdict[item]
This allows for easy use and iteration of your objects. You can always tell the last spawned object because it will have the highest number as the key. This key number will also tell you the total number of spawned objects if you so need it.
I hope this helps you and good luck making your game.

Related

Attribute scaling anomaly

I'm making a 2D game that uses 2D transformations to get an objects position in its environment to the relative position it will be drawn at. As if viewed through a moving camera.
In the zoom function I take the position vector (self.pos) and scale it with a value (z).
Assigning this value to a different attribute (self.zoom_pos)
However the line:
self.zoom_pos.x=self.pos.x*z
changes the original position vector which I don't want to do.
Any explanations?
def zoom(self,z):
print(self.pos.x)
self.zoom_pos.x=self.pos.x*z
self.zoom_pos.y=self.pos.y*z
print(self.pos.x,z)
INPUT
self.pos.x = 100
z = 2
OUPUT
self.zoom_pos.x = 200
self.pos.x = 200
DESIRED OUTPUT
self.zoom_pos.x = 200
self.pos.x = 200
edit: print statements were just for testing
while it is not clear from your question, it seems like you have self.zoom_pos = self.pos somewhere in your code, so these two variables are now pointing to the same object, and any change to one will change the other
an easy fix is to change that line to:
import copy # somewhere at the top
self.zoom_pos = copy.copy(self.pos)
this will only make self.zoom_pos a copy of the object in self.pos, and not the same object.
you should also check Facts and myths about Python names and values

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

Python Processing: How to call multiple of the same class

I am a noob at Python and I am having trouble with this problem. I am trying to create a raindrop scene with the 'drop' class. Right now, the code only calls one drop at a time. How can I call multiple drops at once?
Here is my code:
import random
class drop():
def __init__(self):
# where the drop starts
self.x = random.randint(20,480)
self.y = 0
#how fast the drop falls
self.yspeed = 5
def fall(self):
self.y = self.y+self.yspeed
def show(self):
# color of drop
stroke(1,1,1)
fill(1,1,1)
strokeWeight(1)
line(self.x,self.y,self.x,self.y+10)
def setup():
size(500,500)
global d
d = drop()
def draw():
background(255,255,255)
d.fall()
d.show()
You can create as many instances of a class as you want. For example the following code creates two drops.
d1 = drop()
d2 = drop()
You can also create a list of drops like this -
drops = []
drops.append(drop())
drops.append(drop())
drops[0].fall()
drops[1].show()
I'm a bit rusty with Python, But I would suggest changing draw to accept an array of elements.
So for example, in setup and can generate an array of droplets by doing:
def setup():
size(500,500) #Idk what this does, I assume its another function somewhere else in your code
droplets = [] #The array of droplets we want to render
for x in range(6): #6 is the number of droplets we want.
droplets.append(drop())
#Pass droplets into draw.
def draw(entities):
background(255,255,255) #Again, IDK what this does but its in your code. I assume it is another function somewhere.
for droplet in entities: #Goes through ALL the droplets in the list you passed.
droplet.fall() #Calls these methods on every droplet in the array.
droplet.show()
This is untested, but from here you would somehow pass the droplets array into draw and BOOM you have a bunch of droplets being rendered every draw cycle. I would suggest NOT using global variables. Its just bad practice. You should have another function somewhere that runs all of this, I assume on a time for the draw cycle.

Having trouble With figuring out how to come up with a inventory system in my python game

Im making a little simple survival game in python and pygame and im having trouble coming up with a inventory system. i have been trying to figure it out without any help to challenge myself but its time to ask for help.
i have been playing around with this code
Item class
class item():
def __init__(self,name,amount):
self.name = name
self.amount = amount
Resource class
class wood(pygame.sprite.Sprite):
def __init__(self):
self.x = 700
self.y = 100
self.cut = False
self.frame = 0
def draw(self,display):
self.rect = pygame.Rect((self.x + 38,self.y + 10,20,80))
display.blit(Art_lists.trees_list[self.frame], (self.x,self.y))
if pygame.sprite.collide_rect(self,Global.lion):
if (pygame.key.get_pressed() [pygame.K_SPACE] != 0):
if self.cut == False:
if Global.wood not in Global.inventory:
Global.inventory.append(Global.wood)
Global.oinventory.append('wood')
Global.wood.amount += 1
self.frame = 1
self.cut = True
if self.cut == True:
Global.wood.amount += 0
so when you collide with the node for example a tree when you press space you cut the tree and it adds a object of the items class to a list called inventory and then adds a string to the list oinventory and then oinventory then gets printed on to screen but i cant get a int to be blited with a string bcause it wont accept it. And i really dont think that this is the best way to make an inventory. I want the inventory to just display its items in text on screen with the amount next to it. Like if you have ever played fallout. Sorry if my code looks bad or if my question is not understandable im a novice programmer.
I would set the inventory up as an array. Each value of the array would be an object variable which would change depending on what the user has in their inventory.
You need to break the problem down into multiple parts. One part defines how the world behaves (trees can be chopped, resulting in x wood), another part defines what the player is holding at any particular time while the last part takes that information and renders the inventory to the screen.
That way, you can do other things like: Have NPCs that can chop down wood / have their own inventories / etc. without having duplicate definitions scattered all over the place. Added to which, you'll reduce the amount of data that needs to be saved per-game as the definition of wood doesn't change, whereas how much wood a player is carrying does.
So your GlobalItems would be a dictionary that contains something like...
{Global.wood: {"Name": "Lumber",
"Icon": Art_lists.xxx,
"MaxStack": 20,
"Portable": True}
Global.tree: {"Name": "Ash tree"
"Chop": {Global.wood: 50},
"Portable": False}
}
And your inventory becomes a list/array:
[{"Type": Global.wood, "Count": 10},
{"Type": Global.leather, "Count": 5}]
If you want to handle stacks / preserve item location, you can have multiple inventory entries:
[{"Type": Global.wood, "Count": 20},
None, # Blank space
{"Type": Global.wood, "Count": 15}
]
Then your code to draw the inventory can loop through the inventory list, calculating current tile position for each item, then lookup the Type in GobalItems to determine icon and other useful information.
The separation of concerns has multiple benefits:
It allows different people to work on different parts of the system
Makes the whole thing far less fragile when changes are made - you're either changing what an item is, how many someone has or how it's drawn.
Makes it explicit what information needs to be written to disk for saves
Makes updating much cleaner (What happens if you decide wood should stack in 25 not 20? Now you've only got to change it in one place)
Note that there's probably an argument for separating things that can be picked up from things in the world, but this depends on how your game is intended to work. Using the above method, you'd create an object in the world and tag it as Global.tree. For bug-avoidance, I've added the "Portable" flag to ensure you never end up with a tree in your inventory because you mis-tagged something, you'll get an error instead.
If the player can't drop arbitrary items into the world and pick them up later, There's no reason for portable/non-portable to be mixed up in the same list. You might be better off with a list of things that can end up in player inventory, and a list of things that can appear in the game world.

Why doesn't this circle disappear upon collision?

These seem to be the relevant portions of a slightly larger program. These are essentially the collision detection functions. bubbles is the original list of circles. I want the circle collided with to disappear on impact. I'm fairly sure my problem lies down there in "if collide ==" conditional.
def getDistance(point1,point2):
a= point1.getX()
b= point2.getX()
c= point1.getY()
d= point2.getY()
distance= math.sqrt((b-a)**2 + ((d-c)**2))
return distance
def balloonBubbleCollide(balloon,bubble):
point1 = balloon.getCenter()
point2= bubble.getCenter()
distance= getDistance(point1, point2)
if distance <= 30:
return True
def check(balloon, bubbles, window):
for bubble in bubbles:
collide = balloonBubbleCollide(balloon, bubble)
if collide == True:
bubbles.remove(bubble)
Assume main is running them in proper order. Just don't want to bog the post down with code.
You should not modify a list using remove while iterating over it.
To filter out the colliding bubbles use instead something like:
def check(balloon, bubbles, window):
bubbles[:] = [bubble for bubble in bubbles
if not baloonBubbleCollide(balloon, bubble)]
the code shown will create a first new list of bubble objects to keep (using a list comprehension) and then replace the current content of the bubbles list with it at once.

Categories

Resources