When I try to fire my knife in my game, it doesn't work. I am not very experienced in pygame. This is my first game that I'm making. It worked the first time, but now I try to call a new knife in a function and it doesn't work. I have tried a lot but it won't work. Here's my is my code:
# first I do this
realknifeImg = pygame.image.load('realknife.png')
Realknife_state = "ready"
# then this
def fire_realknife(x, y):
global Realknife_state
Realknife_state = "fire"
screen.blit(realknifeImg, (x, y))
# and finally this
def outro():
RealknifeX = 750
RealknifeY = 400
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
fire_realknife(RealknifeX, RealknifeY)
RealknifeX += 1
The position of the knife is continuously initialized in the outro. Set to position before the function and use the global statement to change the position in the function:
RealknifeX = 750
RealknifeY = 400
def outro():
global RealknifeX, RealknifeY
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
RealknifeX += 1
You need to use a rect for this. It is the standard way of moving
images in pygame.
Instead of having all these variables you can just say:
r_knife_rect = realKnifeImg.get_rect()
r_knife_rect.x = 750
r_knife_rect.y = 400
Then whenever you want to move it call:
r_knife_rect.x += 1
Pygame wont like you using a tuple of (x,y) in your blit, it likes
rect style objects instead. To blit just say:
screen.blit(realKnifeImg, real_knife_rect)
Edit:
Using this also means your knife has a premade hitbox.
Related
I am attempting to make pong using Turtle, I have most things working, however, I would like to implement the ability to hold a key to move the bumpers up and down, rather than having to tap the key multiple times. These are my functions for movement so far.
def lbump_move_up():
x = lbump.ycor()
x += bumpspeed
if x > 240:
x = 240
lbump.sety(x)
def lbump_move_down():
x = lbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
lbump.sety(x)
def rbump_move_up():
x = rbump.ycor()
x += bumpspeed
if x > 240:
x = 240
rbump.sety(x)
def rbump_move_down():
x = rbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
rbump.sety(x)
turtle.listen()
turtle.onkey(lbump_move_up,'w')
turtle.onkey(rbump_move_up,'Up')
turtle.onkey(lbump_move_down,'s')
turtle.onkey(rbump_move_down,'Down')
turtle.onkey(ball_move,'Return')
I don't have a complete answer to this, as I came here looking for one myself. However, I have some progress on it that someone else could finish...
You can create a new class that calls like a recursive function as follows:
class function2:
def __init__(self,fun,args=None):
self.fun=fun
self.args=args
def begin_loop(self):
self.looping=True
self.loop()
def loop(self):
self.fun(self.args)
if self.looping: self.loop()
def end_loop(self):
self.looping=False
Now to tie it to your example, you can convert your functions into function2s and thus call them onkeypress as follows:
l_up=function2(lbump_move_up)
r_up=function2(rbump_move_up)
l_down=function2(lbump_move_down)
r_down=function2(rbump_move_down)
Wn=turtle.Screen()
Wn.onkeypress(l_up.begin_loop,'w')
Wn.onkeypress(r_up.begin_loop,'Up')
Wn.onkeypress(l_down.begin_loop,'s')
Wn.onkeypress(r_down.begin_loop,'Down')
Wn.onkeyrelease(l_up.end_loop,'w')
Wn.onkeyrelease(r_up.end_loop,'Up')
Wn.onkeyrelease(l_down.end_loop,'s')
Wn.onkeyrelease(r_down.end_loop,'Down')
Wn.listen()
So to the issue: RecursionErrors
If you want your function to cause a small change, then you'll need to hold the button for a long time and that causes RecursionErrors - which at least in this case don't seem to be affected by try/except statements.
Once again, sorry for the incomplete solution, but I feel like you can probably get away with larger movements in pong.
I had the same issue, but found the "onkeypress" function in the Turtle documentation (https://docs.python.org/3/library/turtle.html#turtle.onkeypress), which resolved the issue for me.
turtle.onkeypress(fun, key=None)
"onkeypress", will move the turtle as long as the key is pressed, while "onkey" will move it once when the key is released.
Example:
from turtle import Turtle, Screen
turtle = Turtle("turtle")
turtle.penup()
screen = Screen()
screen.listen()
def move_forward():
turtle.forward(20)
def move_backward():
turtle.backward(20)
screen.onkeypress(move_forward, "w")
screen.onkey(move_backward, "s")
screen.exitonclick()
as discussed in the title I am having issues with masking identical images.
#initalising the masks
Invader1= pygame.image.load('Space_invaders_character_1_1.png').convert_alpha()
Invader1= pygame.transform.scale(Invader11, (40,30))
Invader1_mask = pygame.mask.from_surface(Invader11)
Invader1_mask= Invader11_mask.scale((70,40))
Invader2= pygame.image.load('Space_invaders_character_2_1.png').convert_alpha()
Invader2= pygame.transform.scale(Invader21, (40,30))
Invader2_mask = pygame.mask.from_surface(Invader21)
Invader2_mask= Invader11_mask.scale((70,40))
Invader3= pygame.image.load('Space_invaders_character_3_1.png').convert_alpha()
Invader3= pygame.transform.scale(Invader31, (40,30))
Invader3_mask = pygame.mask.from_surface(Invader31)
Invader3_mask= Invader11_mask.scale((70,40))
#drawing characters
def drawEnemies (invX,invY):
for num in range (1,11):
invX = invX + 50
gameDisplay.blit(Invader32, (invX,invY))
gameDisplay.blit(Invader32, (invX,invY-50))
gameDisplay.blit(Invader22, (invX,invY-100))
gameDisplay.blit(Invader22, (invX,invY-150))
gameDisplay.blit(Invader12, (invX, invY -200))
while lives > 0:
offset = (bulletX -invX, bulletY - invY)
result = Invader11_mask.overlap(bullet_mask, offset)
Of course this isn't all my code, however, I hope you see what I am attempting to do. In essence I am attempting to loop to create a specific Invader (yes from space invaders), however, the masks are either not being created with the other invaders or aren't moving. Can someone please help me?
Thanks.
The meaningful answer to your problem is to stop what your doing right now and start using the Sprite and Group classes together with the collide_mask function.
You don't want to create several global variables for each thingy in your game. You want instances of classes (you usually use Sprite), and add them to a list (usually a Group).
So, create a class for your invaders that inherits from Sprite and give them a mask attribue, something like this:
class Invader(pygame.spriteSprite):
def __init__(self, image, pos):
super().__init__()
self.image = image
self.rect = image.get_rect(topleft=pos)
self.mask = pygame.mask.from_surface(image)
def update(self):
pass # handle movement
Create a Group for your bullets and one for your invaders, then you can check the collision with:
pygame.sprite.groupcollide(bullets, invaders, True, True, pygame.sprite.collide_mask)
I'm in the process of creating a basic game in pygame at the moment, and one part of that is the procedural generation of new areas as you go off the screen. As a test, I'm looking to generate an object once per area by defining its variables, and then save that area's object within the class for if you come back to it later. Here's what I have at the moment:
#area_gen is set to "true" if you move to a new area
#swidth and sheight are set to the size of the screen
#x_area and y_area are defined as you change areas, acting as sector coordinates
#Red is defined in globals
class areas:
def __init__(self, coords):
self.coordinates = coords
self.generated = False
def gen_objects(self):
if not self.generated:
self.objs = []
obj_type = "test object"
center_x = random.randrange(105, swidth-25)
center_y = random.randrange(25, swidth - 175)
self.objs.append([obj_type, center_x, center_y])
self.generated = True
#Within The Game Loop
if area_gen == "true":
coords = str(str(x_area) + " " + str(y_area))
area = areas(coords)
area.gen_objects()
for thing in area.objs:
if thing[0] == "test object":
pygame.draw.rect(screen, Red, (thing[1], thing[2], 250, 250))
#Bottom of the Game Loop
area_gen = "false"
What I thought the self.generated variable would do was stop the new object generation if one already existed, but that doesn't seem to be working. The square still generates at a new location even if the area has already been visited. My knowledge on classes is relatively limited, so I'm a bit stuck as to where to go from here.
Pass area_gen into the constructor for the areas class, and set self.generated = area_gen. Does this work? I can't see enough of your code to know, to be honest.
So i have this platforming game that im programming in pygame. I have a jump method and i want to add a dely between jumps. That way i dont keep jumping into the ceiling. Heres the code
def jump(self):
if (self.onGround == True):
return
self.velocity = 10
self.onGround = False
This is what calls it in the main game loop:
if (event.key==pygame.K_UP):
player.jump()
Keep track of the last time you jumped, or of the next time you're allowed to jump (with pygame.time):
def __init__(self, …):
# ...
self.next_jump = 0
def jump(self):
if pygame.time.get_ticks() < self.next_jump:
return
# ...
self.next_jump = pygame.time.getticks() + 500
Depending on which type of loop you've gone with (traditional event loop, frame-rate-limited loop, unlimited-frame-rate loop) there may be better answers involving, e.g., setting a timer or using a Clock, but this will work with any type.
I have a ball generator, that "generates" and adds balls(circles) to the simulation.
The ball is to be removed when it hits a static poly in list s_boxes.
This is done by a collision handler ball_wall_collision.
The Error:
The following pop-up window does what it's name says, it pops-up
My code:
Ball Generator
class BallGenerator:
def __init__(self, min_y, max_y, x):
self.min = min_y
self.max = max_y
self.x = x
self.counter = 0
def bowl(self, balls):
global ball_bowled
y = random.randint(self.min, self.max)
pos = to_pymunk((self.x,y))
r = 10
m = 15
i = pm.moment_for_circle(m, 0, r)
b = pm.Body(m,i)
b.position = pos
f_x = random.randint(-600000,-400000)
b.apply_force( (f_x,0.0),(0,0) )
ball = pm.Circle(b, r)
ball.elasticity = 0.75
ball.friction = 0.95
balls.append(ball)
space.add(ball,b)
print 'bowled'
ball_bowled += 1
def handle(self, balls):
if self.counter == FPS:
self.bowl(balls)
self.counter = 0
self.counter += 1
Collision handler
def ball_wall_collision(space, arb, balls, s_boxes):
shapes = arb.shapes
boxes = [box[0] for box in s_boxes] # Get walls
ball = None
wall = None
for ba in balls:
if ba in shapes:
ball = ba
break
for box in boxes:
if box in shapes:
wall = box
break
if wall and ball:
print 'removing'
space.remove(ball, ball.body) # Where the runtime problem happens
balls.remove(ball)
print 'removed'
return False
else:
return True
space.add_collision_handler(0,0,begin=ball_wall_collision,
balls=balls,s_boxes=s_boxes) # Other args to function
What am I doing wrong in the collision handling??
Am I missing something in the call to space.remove?
Is the function not working as I want it to??
Or is the error elsewhere (which I don't think it is)...
It looks like the problem is that you try to remove objects from the space in a collision handler during the simulation step.
Instead you can try with either manually collect all the balls into a list and then call remove after the step, or queue up removes with the post step callback like this:
space.add_post_step_callback(space.remove, ball)
space.add_post_step_callback(space.remove, ball.body)
(untested code)
I should probably try and make this more obvious in the API docs.. I wonder if it would be a good idea to automatically schedule the remove until end of step, or the less intrusive option, trigger a assert in python so you dont get the c++ error.