I'm new to pygame and am making a zombie running game. I am controlling the human with the arrow keys and I would like to have a zombie run toward the player's position when the zombie is first created. I am unsure how to determine the players position to set the zombie in the correct direction. The zombie doesn't have to follow the human, just head in the direction of the players position when the zombie is created.
class Human(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("human.jpg")
self.image=self.image.convert()
self.rect=self.image.get_rect()
self.x=100
self.y=430
self.dx=50
self.dy=0
def update(self):
keys=pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.x-=self.dx
if keys[pygame.K_RIGHT]:
self.x+=self.dx
self.rect.center=(self.x,self.y)
class ZombieChase(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("zombie.jpg")
self.image=self.image.convert()
self.rect=self.image.get_rect()
self.reset()
self.dx=15
self.dy=15
#self.xStart=random.randrange(0,screen.get_width())
def update(self):
self.rect.centery+=self.dy
self.rect.centerx+=self.dx
if self.rect.top>screen.get_height():
self.reset()
if self.rect.right>screen.get_width():
self.reset()
if self.rect.left<0:
self.reset()
def reset(self):
self.rect.top=0
speed=random.randrange(20,36)
xStart=random.randrange(0,screen.get_width())
yStart=0
#humanx, humany !!!!!!this is where i don't know what to put!!!!!
try:
self.dx=(humanx-xStart)/speed
except ZeroDivisionError:
self.dx=0
try:
self.dy=(humany-yStart)/speed
except ZeroDivisionError:
self.dy=1
self.rect.centerx=xStart
Sorry if the tabbing isn't right.
If you subclass pygame.sprite.Sprite the location is zombie.rect . For coords, zombie.rect.center or zombie.rect.topleft
Related
I am learning PyQt5 recently and have problems when I want to delete a widget in QScrollArea. Is there an elegant way to visit the element in QScrollArea and delete it when the "delete" button in that element is clicked? Thank you for any help!
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
self.layout.addWidget(QPushButton('Delete'))
self.setLayout(self.layout)
# connect options
connect_options()
def connect_options(self):
pass
class MyList(QScrollArea):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.widget = QWidget()
for x in range(10):
self.layout.addWidget(MyWidget(str(x)))
self.widget.setLayout(self.layout)
self.setMinimumSize(1024, 500)
self.setWidget(self.widget)
First I would suggest you look into QListWidget, It will provide you various functions to handle situations like this.
for your problem, you will need to delete the widget from its parent layout & then delete it from the GUI
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
del_btn = QPushButton('Delete')
del_btn.clicked.connect(self._delete) # connect the click event to your delete function
self.layout.addWidget(del_btn)
self.setLayout(self.layout)
# connect options
self.connect_options()
def connect_options(self):
pass
def _delete(self):
# here you will delete your widget
parent_layout = self.parent().layout()
parent_layout.removeWidget(self) # remove the widget from its parent layout
self.deleteLater() # lets Qt knows it needs to delete this widget from the GUI
del self
I want a black window to fade in. When in fullscreen, it perfectly works but I need a specific size and there when its opened, it first appears black before it becomes transparent and the fading starts. Do you have any ideas how to achieve the same smooth effect as for the fullscreen version?
import tkinter as tk
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.attributes("-alpha",0.0)
#self.parent.attributes("-fullscreen",True)
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
alpha = self.parent.attributes("-alpha")
if alpha < 1:
alpha += .01
self.parent.attributes("-alpha", alpha)
self.after(100, self.fade_in)
if __name__ == "__main__":
root = tk.Tk()
root.bind("<Escape>",lambda e: root.destroy())
Fader(root).pack(fill="both", expand=True)
root.mainloop()
You can use withdraw() to hide the window and deiconify() to show to window later on and increase the alpha. But it seems to not work unless you update the tasks or wait for the window to be visible.
Method 1:
Was able to fix this by using update_idletasks(), like:
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.attributes('-alpha',0.0)
self.parent.withdraw() #hiding the window
#self.parent.attributes("-fullscreen",True)
self.parent.update_idletasks()
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
self.parent.deiconify() #bringing it back
..... #same code
Method 2:
Or like said by acw1668, you can use wait_visibility(), like:
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.wait_visibility(self.parent)
self.parent.attributes('-alpha',0.0)
self.parent.withdraw()
# self.parent.attributes("-fullscreen",True)
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
self.parent.deiconify()
...... #same code
A bit more about wait_visibility():
wait_visibility(window=None)
Wait for the given widget to become visible. This is typically used to wait until a new toplevel window appears on the screen. Like wait_variable, this method enters a local event loop, so other parts of the application will still work as usual.
A bit more about update_idletasks():
update_idletasks()
Calls all pending idle tasks, without processing any other events. This can be used to carry out geometry management and redraw widgets if necessary, without calling any callbacks.
Source :- https://effbot.org/tkinterbook/widget.htm
I'm trying to put my window in the top left corner by runing the following code:
import sys
from PyQt5 import QtGui
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
self.resize(640, 480)
self.topLeft()
self.show()
def topLeft()(self):
frameGm = self.frameGeometry()
topLeftPoint = QApplication.desktop().availableGeometry().topLeft()
frameGm.moveTopLeft(topLeftPoint)
self.move(frameGm.topLeft())
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
But doing so, I get a gap between my window and the left-side of the screen like this :
Do you know where it comes from and how to prevent it?
This happens because when a window is shown the first time on the screen, some things (lots, actually) happen between show() and the moment where the window is actually mapped on the screen.
In order to achieve what you want, you need to delay the movement by some amount of time (usually, on "cycle" of the event loop is enough). Using a single shot QTimer with 1 timeout is normally fine.
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
self.resize(640, 480)
QTimer.singleShot(1, self.topLeft)
self.show()
def topLeft(self):
# no need to move the point of the geometry rect if you're going to use
# the reference top left only
topLeftPoint = QApplication.desktop().availableGeometry().topLeft()
self.move(topLeftPoint)
So i created a class that runs a big calculation in a seperet thread.
This is what i expected to see:
And this is what i do see:
This is the frame class
class ThreadFrame(wx.Frame):
def __init__(self,parent=None):
wx.Frame.__init__(self,parent=parent)
self.frame = wx.Frame(None, title='Bitte Warten',style=wx.FRAME_NO_TASKBAR)
self.frame.SetSize(500,100)
self.panel=wx.Panel(self.frame)
self.parent=parent
self.WaitLbl=wx.StaticText(self.panel,-1)
self.WaitLbl.SetLabel('Geotags werden ausgelesen und abgerufen.')
self.progress = wx.Gauge(self.panel,size=(500,30), range=self.parent.list_ctrl.GetItemCount())
self.btn = wx.Button(self.panel,label='Abbrechen')
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.Sizer=wx.BoxSizer(wx.VERTICAL)
#Add Widgets LeftSizer
self.Sizer.Add(self.WaitLbl,0,wx.ALL|wx.CENTER,5)
self.Sizer.Add(self.progress,0,wx.ALL,5)
self.Sizer.Add(self.btn,0,wx.ALL|wx.CENTER,5)
self.panel.SetSizer(self.Sizer)
self.Sizer.Fit(self.panel)
self.panel.Layout()
self.Centre()
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
#self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self.frame, self.parent)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
print('Thread lebt noch')
self.mythread.terminate() # Shutdown the thread
print('Thread wird beendet')
self.mythread.join() # Wait for it to finish
self.Close()
And this is the thread where the calculation is running
class TestThread(Thread):
def __init__(self,parent_target,toplevel):
Thread.__init__(self)
self.parent = toplevel
self.ownparent=parent_target
self.stopthread = False
self.start() # start the thread
def run(self):
print('Thread gestartet')
i=0
while self.stopthread == False:
#if calculation is not finished:
#do calculation and count i one up
evt = progress_event(count=i)
#Send back current count for the progress bar
try:
wx.PostEvent(self.ownparent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
i=i+1
else:
print('Thread Terminated')
self.terminate()
def terminate(self):
self.stopthread = True
And this is how i call the class from my main programm:
frame=ThreadFrame(self)
The main program also has a frame open. So this is a frame which is opend and then starts a thread which does calculation and then stops.
I think thats all that is to know. I replaced the caluclation with speudo code because my brain hurts and i cant come up with a plachold right now. But i feel like between all the fiting and sizers and panels and frames i went somewhere wrong. And i totaly dont look through all of this stuff at the moment.
The code you show doesn't seem to correspond to the screenshots. Whatever problem with the layout you might have, you should still have "Bitte warten" in the frame title, but your screenshot doesn't even show this. Either you're not executing the code you show at all, or you create some other frame elsewhere in your code which you see here. Or, of course, you've uploaded a wrong screenshot or code version. But something just doesn't fit here.
So im not sure what caused the problem. I started from scratch and now it works. This time i didnt use a new frame because the class itself is allready a frame.
class GeoThreadFrame(wx.Frame):
def __init__(self, radsteuer):
wx.Frame.__init__(self,parent=radsteuer.frame,style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP|wx.FRAME_NO_TASKBAR)
panel = wx.Panel(self)
self.SetWindowStyle(wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP)
self.progress = wx.Gauge(panel,size=(300,30), pos=(10,50), range=radsteuer.list_ctrl.GetItemCount())
self.btn = wx.Button(panel,label='Abbrechen', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
panel.Center()
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Center()
self.Show()
self.mythread = GeoLocationThread(self, radsteuer)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
#time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
#self.mythread.join(3) # Wait for it to finish
self.Destroy()
class GeoLocationThread(Thread):
def __init__(self,parent_target,mainparent):
Thread.__init__(self)
self.parent = parent_target
self.mainparent=mainparent
self.stopthread = False
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
i=0
while self.stopthread == False:
if i < self.mainparent.list_ctrl.GetItemCount():
self.calculation(i)
evt = progress_event(count=i)
i=i+1
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
def calculate(self,i):
#your calculation here
I've been working on creating a small, space invaders style game in pygame. I've almost reached the end however, I want to make it so if the enemy ships collide with my ship, a collision is detected and the game ends.
So far I have the code for detecting when a bullet and an enemy ship collides, however when I tried to rewrite this for a enemy/player collision it doesnt work as expected, so I think im doing something incorrect.
Code in question:
for block in block_list:
player_hit_list = pygame.sprite.spritecollide(block, player_list, True)
for player in player_hit_list:
explosion.play()
block_list.remove(block)
player_list.remove(player)
all_sprites_list.remove(block)
all_sprites_list.remove(player)
if block.rect.y < +10:
block_list.remove(block)
all_sprites_list.remove(block)
Full code: http://pastebin.com/FShPuR6A
Is anyone able to tell my why the code I have isnt functioning?
Thanks a lot
class Block(pygame.sprite.Sprite):
def __init__(self, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("spaceship.png")
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x=0
self.y=0
self.image = pygame.image.load("alien.png")
self.rect = self.image.get_rect()
def update(self):
pos = pygame.mouse.get_pos()
self.rect.x = pos[0]
At first glance, you have a Block class, with a picture of a spaceship that moves like an Alien. The Player class, has an alien image, and moves with the mouse.
My advice would be to rename the classes so that you will know what is what. It will be easier to spot the error.
EDIT:
It also looks like your Player does not move at all:
def render(self):
if (self.currentImage==0):
screen.blit(self.image, (self.x, self.y))
You update the player via self.rect.x, but you draw using self.x and self.y.