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.
Related
I'm trying to make a procedure that updates the stacking order of randomly created windows based on their y value (so that things that are closer to the top of the screen display under things that are lower down).
Basically I have a button that creates a window by calling a class statement. Each time a window is created, its id and y value get put in an array, which would get sorted and looped through to update the stacking order.
I figured having the id of each window that is created would be enough to change the variables of specific windows, using the id of the window.lift(), but it looks like you can't point to an object with its id like that. If I'm not mistaken I can't just use the class name because I don't want to change the whole class, just the specific window.
Anybody know what I can use instead of the id to get this to work? Is it just impossible the way I have it planned?
Here's my code in case it's helpful, unfinished because obviously it doesn't work:
import tkinter as tk
import random
# creates a new window
def makewndw(event):
a = testwindow()
# create main window - click to create a new window
mainwindow = tk.Tk()
mainwindow.bind('<Button-1>', makewndw)
# this sorts the windows
class stacker():
stackerwindow = tk.Toplevel()
yarray = [{'ident': 0, 'ypos': 0}]
def arupdate(self):
#sort array by ypos
self.yarray.sort(key=lambda x: x.get('ypos'))
# print(self.yarray)
# get length of array
arlen = len(self.yarray)
# loop through array between pos 1 and end
for x in range(1,arlen):
# extract dictionary from that position
tempex = self.yarray[x]
# populate temp list with values from tempex
templist = []
for i in tempex.values():
templist.append(i)
# get just the id
tempid = templist[0]
# use id to update stacking order
tempid.lift()
# ^^^ this is where i'm suck because clearly this isn't working
self.stackerwindow.after(10, self.arupdate)
class testwindow():
def __init__(self):
# create a window
self.window = tk.Toplevel()
self.y = random.randrange(100, 500)
self.window.geometry('200x200+800+{y}'.format(y=str(self.y)))
# give info to sorter
self.infdict = {'ident': id(self.window), 'ypos': self.y}
stacker.yarray.append(self.infdict)
st = stacker()
st.arupdate()
mainwindow.mainloop()
I'm a beginner so I'm sorry if the answer here is obvious, and sorry about how messy I'm sure my code is.
I want to spawn an enemy into the game at random intervals of 1-5 seconds. To prevent the gameplay part of the program (moving character etc.) from pausing whenever there is a sleep, I have split the whole code into 2 methods so that I can have 2 threads- one that sets enemyspawn True every 1-5 seconds and another one which controlls the character.
Sadly, either the 2nd method doesn't change enemyspawn in the first method or threading outputs an error. I have read about and tried retrieving the value from EnemySpawn() before the if statement but that didn't work (or I just did it wrong). I have also tried turning enemyspawn to a global variable. Didn't change anything.
def Gameplay():
width=80
height=80
y = 720/2+height/2
x = 720/2+width/2
speed=2
enemyspawn = False
while True:
#controll character here
if enemyspawn:
enemyspawn=False
print(enemyspawn) #Spawn enemy here later
window.blit(bg, [0,0])
pygame.draw.rect(window,(100,100,100),(x,y,width,height))
pygame.display.update()
def EnemySpawn():
enemyspawn = EnemySpawn() #idrk about this line
while True:
sleep(randint(1,5))
enemyspawn=True
print(enemyspawn)
return enemyspawn
Gameplay = threading.Thread(target=Gameplay)
Gameplay.start()
EnemySpawn = threading.Thread(target=EnemySpawn)
EnemySpawn.start()
The threading error message:
line 51, in EnemySpawn
enemyspawn = Gameplay(enemyspawn)
UnboundLocalError: local variable 'enemyspawn' referenced before assignment
Another error message:
line 51, in EnemySpawn
enemyspawn = EnemySpawn()
TypeError: 'Thread' object is not callable
You wouldn't want to call a function from within the same function (a recursive function) in this situation. A better approach might be:
def spawn_enemy():
# code that makes an enemy
print("an enemy has been spawned")
return True # would be "return enemy" after you create your enemy entity
def EnemySpawnThread():
enemy_list = [] # to maintain records of all enemies made
while True: # make enemies forever
sleep(randint(1,5))
enemy_list.append(spawn_enemy()) # call our function we made above which spawns enemies
Currently this would just make a list like [True, True, True, ...], but eventually you would probably define an enemy class and it would become [enemy_object, enemy_object, ...] which is preferable because the best way I've found to delete objects is to store them in lists and then del enemy_list[index] to delete them (for instance when the enemy dies so it doesn't keep using memory). Additionally it gives you the power to iterate over like:
for enemy in enemy_list:
enemy.move()
enemy.attack()
enemy.die()
Which is why you would want enemy to be a class that has methods like shown above.
As a side note, you would likely want to have enemy_list as a global variable (or in your main function) so that all your threads can access it. Otherwise you will need to implement the queue.Queue() built in standard with python.
i wanna loop a line function in processing using python. it works but without any motion. i wanna loop line like this:
https://www.youtube.com/watch?v=0KUg9dcIFtM
but in python this not work. it is static.
This is my code:
def setup():
size(400,600);
background(255);
def draw():
x=0;
num=600;
andx=0;
for x in range(0,600,20):
line(x,0,x,600);
x+=20;
andx+=20;
if andx>400:
andx=0;
why i have no motion?
screen of the sketch
If you were trying to follow that video I can see how it was somewhat confusing (given all of the commented out code). A working Python equivalent is:
andx = 0 #global variable to determine placement of final line
def setup():
size(400,600)
frameRate(5)
def draw():
global andx
background("#EC4DF5")
stroke(50)
for x in xrange(0,andx,20):
line(x,0,x,height)
andx += 20
if andx > width:
andx = 0
draw() is what actually draws in an animation loop. Any drawing function such as line() is buffered with the display only redrawn at the end of the call to draw().
For tutorials, it would be better to see the Python-based ones here rather than Java ones on YouTube.
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.
I am trying to make a simple space invaders game and a problem I have run into is getting things t happen at the same time. I have binded the shooting action to the canvas of the game so that when you click a function is called. I would like it so that this function can be called multiple times at once so that multiple "lasers/bullets" can be seen on the screen at any one time. At the minute when you click and a "laser/bullet" is already on screen, the previous one disappears and a new one appears. CODE:
class Game1():
def __init__(self, xcoord1=380, ycoord1=550, xcoord2=400, ycoord2=570):
self.Master = Master
self.Master.geometry("800x600+300+150")
Game1Canvas = Canvas(self.Master, bg="black", height=600, width=800)
Game1Canvas.place(x=0, y=0)
self.Canvas = Game1Canvas
self.Canvas.bind("<Button-1>", self.Shoot)
self.Ship = self.Canvas.create_rectangle(self.xcoord1, self.ycoord1, self.xcoord2, self.ycoord2, fill = "red")
def Shoot(self):
self.LaserLocation = 0
for self.LaserLocation in range(0 , 112):
Master.after(1, self.Canvas.create_rectangle(self.xcoord1, self.ycoord1 - (self.LaserLocation * 5), self.xcoord2 - 5, self.ycoord2 - (self.LaserLocation * 5), fill = "pink", tag=str(CurrentTag)))
Master.update()
self.Canvas.delete(str(CurrentTag))
This is a much more "dumbed" down version of the code at the minute because I've been trying a bunch of different ways to get this working and it's a mess. I am aware of the multiprocessing and threading imports and I have tried them both but am unable to get them working for my code. If someone could reply back with a solution I would be very grateful. Cheers.
You don't need to use multithreading or multiprocessing. You also don't need (nor want) to be drawing new rectangles every millisecond, or multiple times per millisecond.
The solution is to have your Shoot function merely create a single rectangle, and add it to a list. Then, using a simple animation mechanism, iterate over the list and move each bullet up one or two pixels. You do this by creating a function that calls itself every 20-30 ms.
The solution looks something like this:
def Shoot(self):
laser = self.Canvas.create_rectangle(...)
self.lasers.append(laser)
def do_animation(self):
# make a copy of the list of lasers to iterate
# over, so we can remove items from the original
# list when they go off screen
lasers = self.lasers[:]
for laser in lasers:
# get current coordinates of this laser
(x0,y0,x1,y1) = self.canvas.bbox(laser)
if x1 < self.canvas.winfo_height():
# if it is not off screen, move it up
self.canvas.move(laser, 0, -10)
else:
# if it IS off screen, delete it
self.canvas.delete(laser)
self.lasers.remove(laser)
self.after(30, self.do_animation)
The above will move the lasers every 30 milliseconds (about 33 fps).