Pygame Jumper collision detection not working - python

I am trying to make a game similar to what is seen in google chrome when the internet is down for my A Level computer science course work. I have run into an issue regarding collision detection and any help would be greatly appreciated.
When i try to declare more than one platform to stop player movement (collision detection) all platforms stop stopping player movement. But when i have only one platform stopping player movement it will work.
All of my code is located here: https://github.com/VincenzoLaRoche/ComputerScienceCourseWork

Your problem is that your are handling the platforms completely separate. So if you stand on one, you are not touching the other one so it makes you fall. For this to stop, you have to modify the t1o player methods collision_detect and do like so:
def collision_detect(self, platform):
if self.x > platform.x and self.x < platform.x2:
if self.y + 40 == platform.y:
return True
else:
return False
def do(self):
self.keys()
self.move()
self.draw()
c1 = self.collision_detect(platform(0, 500, 800, 10))
c2 = self.collision_detect(platform(0, 480, 400, 10))
if c1 or c2:
self.yVel = 0
Constants.CANJUMP = True
else:
self.yVel = 5
Constants.CANJUMP = False

Related

pygame image.blit not working from one class but is working from the other [duplicate]

This question already has answers here:
Why is nothing drawn in PyGame at all?
(2 answers)
Why is my PyGame application not running at all?
(2 answers)
Closed 11 months ago.
I have wrecked my mental health going over and over and over this code. I have created two classes that are nearly identical yet my images from the second class are not rendering on the screen even though my first class's images are rendering perfectly fine. I've went over and over them to see if I can spot the difference between them that could be causing this but so far nothing. I've also tried swapping class one's image for class two's image and it renders the image perfectly fine from class one, so not an issue with the image itself. I'm lost. Any help from the good people here on stack would be beyond greatly appreciated. I'm sure its something stupidly simple that I'm overlooking but if I look over this code anymore I will go permanently cross-eyed.
Here is the snippet. TIA.
class one:
def __init__(self, num_value, bool, x, y):
self.num_value = num_value
self.bool = bool
self.x = x
self.y = y
self.one_image = pygame.image.load(os.path.join('Program_Assets', 'one.png'))
self.image_size = (250, 350)
self.one_image = pygame.transform.scale(self.one_image, self.image_size)
if bool == False:
self.one_image = pygame.transform.rotate(self.one_image, 90)
_VARS['surf'].blit(self.one_image, (self.x, self.y))
if (self.num_value == "1k"):
self.num_value = 1000
elif (self.num_value == "2k"):
self.num_value = 2000
elif (self.num_value == "10k"):
self.num_value = 10000
def setxy(self, x, y):
self.x, self.y = x, y
_VARS['surf'].blit(self.one_image, (self.x, self.y))
def getxy(self):
return self.x, self.y
class two:
def __init__(self, bool, x, y):
self.bool = bool
self.x = x
self.y = y
self.two_image = pygame.image.load(os.path.join('Program_Assets', 'two.png'))
self.image_size = (265, 175)
self.two_image = pygame.transform.scale(self.two_image, self.image_size)
if bool == True:
self.two_image = pygame.image.load(os.path.join('Program_Assets', 'two_alt.png'))
self.two_image = pygame.transform.scale(self.two_image, self.image_size)
_VARS['surf'].blit(self.two_image, (self.x, self.y))
def setxy(self, x, y):
self.x, self.y = x, y
_VARS['surf'].blit(self.two_image, (self.x, self.y))
def getxy(self):
return self.x, self.y
# GLOBAL VAR, Using a Dictionary.
_VARS = {'surf': False}
# This is the main loop, it constantly runs until you press the Q KEY
# or close the window.
# CAUTION: This will run as fast as your computer allows,
# if you need to set a specific FPS look at tick methods.
def main():
pygame.init() # Initial Setup
_VARS['surf'] = pygame.display.set_mode(SCREENSIZE)
is_running = True
# The loop proper, things inside this loop will
# be called over and over until you exit the window
clock = pygame.time.Clock()
t1 = two(True, 250, 350)
o1 = resistor("10k", True, 10, 10)
o2 = resistor("10k", False, 10, 200)
o1_selected = False
o2_selected = False
t1_activated = False
while is_running:
_VARS['surf'].fill(WHITE)
drawGrid(8)
o1_x, o1_y = o1.getxy()
o2_x, o2_y = o2.getxy()
t1_x, t1_y = t1.getxy()
clock.tick(30)
pygame.display.update()
pygame.quit()
if __name__ == '__main__':
main()
EDIT: just posted the loop this time -- all I did here was add your setxy()'s so that the blit ran -- again I get all the images...
Of course to get it work work on my system I had to use different images, add a SCREENSIZE, and WHITE. I commented out the drawGrid. I also have an event handler to QUIT -- but that should have nothing to do with displaying the images.
I did not make any intentional changes to your logic - just what was needed to get a display.
while is_running:
_VARS['surf'].fill(WHITE)
#drawGrid(8)
o1_x, o1_y = o1.getxy()
o2_x, o2_y = o2.getxy()
t1_x, t1_y = t1.getxy()
o1.setxy(o1_x, o1_y)
o2.setxy(o2_x, o2_y)
t1.setxy(t1_x, t1_y)
clock.tick(30)
pygame.display.update()
for event in pygame.event.get():
# print(pygame.event.event_name(event.type))
if event.type == pygame.QUIT:
is_running = False
print("Exit...")
I finally figured it out. The program was calling the other two images further down in my while loop via:
o1.setxy(o1_x, o1_y)
o2.setxy(o2_x, o2_y)
My first clue at this was when I set my pygame.display.update() nearer the top of my while loop based on some of the comments and suggestions I received here. When I ran my code with pygame.display.update() at the top of my while loop I lost my o1 and o2 images. Based on this finding I suspected that the images were being rendered somewhere further down in my while loop. It was at this point I noticed the o1.setxy & o2.setxy and finally had my Eureka! moment. I promptly added a
t1.setxy(t1_x, t1_y)
and voila! I now have a third image for my instance of my two class!!! Mental health is partially intact now and my eyes are no longer crossed. Thank you all so much for your help in flushing this out and achieving my renders.

Screen looks like it's sinking due to weird collision/jumping values that affect camera movement

I'm trying to implement vertical camera movement in my PyGame platformer (horizontal camera movement works fine, almost definitely irrelevant to this) but the screen always appears to constantly be sinking unless the player jumps (but when the player finishes his jump, it starts sinking again). Here's the code:
if self.onGround == False: # falling
self.vermo += 0.4 # player's vertical momentum increasing as he falls
if self.onGround == True:
self.jump = False
self.fall = False
if self.vermo > 10: # can't fall too fast
self.vermo = 10
if self.vermo > 0 and self.collideWithBlock == False: # checks if the player is not on the ground but has also past climax/zenith of jump
self.fall = True
if keys[pygame.K_SPACE] and self.onGround == True: # makes player jump but only if touching the ground
self.vermo = -12
self.jump = True
if self.fall == True and self.onGround == False: # if the player is not on the ground but has past the climax.zenith of his jump, the camera should follow the player downward
for sprite in self.game.all_sprites: # this moves every sprite on the screen to give the illusion of camera movement
sprite.rect.y -= self.vermo
if self.jump == True: # if the player is shooting upwards (jumping) then the camera should follow him upward
for sprite in self.game.all_sprites:
sprite.rect.y -= self.vermo # I thought this should be += but apparently not
The reason this is happening is due to self.vermo looping the values below. The problem is, I have no idea what causes this.
These are the values:
0
0.4
0.8
1.2000000000000002
0
Here is the basic code for a camera class:
class Camera():
def __init__(self, width, height):
self.camera = pg.Rect(0,0,width,height)
self.width = width
self.height = height
def apply(self,entity):
return entity.rect.move(self.camera.topleft)
def update(self, target):
x = -target.rect.x + width//2
y = -target.rect.y + height//2
x = min(0, x)
y = min(0, y)
x = max(-(self.width - width), x)
y = max(-(self.height - height), y)
self.camera = pg.Rect(x,y,self.width,self.height)
Every time you update, you have to apply the update method, with target=player.
This will adjust the camera according the player's new position.
def update(self):
self.all_sprites.update()
self.camera.update(self.player)
When you blit the other objects to the screen, you create the illusion the player is moving.
for sprite in self.all_sprites:
self.screen.blit(sprite.image, self.camera.apply(sprite))
Keep in mind that the width and height parameters passed into the camera constructor are the width and height of the level, not the width and height of the screen.
I would try implementing this and see if it fixes your sinking problem.
You could also try adding this to your existing code:
if self.onGround == True:
self.vermo = 0
self.jump = False
self.fall = False
This will ensure that when the player is on the ground, no vertical momentum.
I really couldn't tell you why, but in this line of code:
if self.vermo > 0 and self.collideWithBlock == False:
self.fall = True
the 0 needs to be replaced with 1.6. Again, no idea why, but it works. I don't question the ways of the Python gods, but by all means if you know why this is I encourage you to comment it here.
The camera, as it is now, still has some issues, but I know why these issues are occurring and I'm currently working on fixing them.

How to create free game collision with pyglet?

I have started working on a 2d game that is comparable to Terraria in terms of what I am dealing with. My problem is as follows The world is made of tiles/blocks then there is the player. I can't seem to find a way to make the player collide with the tiles, why not a set ground level? because if there isn't a tile under the player he should fall and what if the players on a hill? So long story short I need a way to make the player collide with the tile beneath them.
So here is what I have tried: making a simplified grid to verify if the is a tile below, a for loop where I compared all the tiles to the players feet but this was too much data to feed and I couldn't find a way to say check the tile under, above, left, and right of the player and only those positions. I am going to try to show useful code but it will be a bit jumbled seeing as I cant pas't all the files in.
class Physics:
#classmethod
def fall(cls, dt, o, world, mod):
if world.grid_fill[(o.grid_pos[0], o.grid_pos[1]-1)] == "Filled":
o.falling = False
else:
o.y -= o.height//8*dt
o.update_pos(o.x, o.y)
#classmethod
def run_physics(cls, dt, o, world):
for obj in o:
Physics.fall(dt, obj, world, None)
# player and Physics are in 2 different files as is world and the main loop
class Player:
def __init__(self, sprite, x, y, world):
self.img = sprite
self.img.set_position(x, y)
self.key_handler = key.KeyStateHandler()
self.type = "Player"
self.x = x
self.y = y
self.pos = (self.x, self.y)
self.grid_pos = world.pix_cords[self.pos]
self.width = self.img.width
self.height = self.img.height
self.speed = self.width//4
self.falling = False
def update_pos(self, x, y):
self.img.set_position(x, y)
def draw(self):
self.img.draw()
def check_fall(self, tile):
if self.y - self.height//2 == tile.y + tile.height:
return False
else:
return True
def update(self):
if self.key_handler[key.A]:
self.x -= self.speed
if self.key_handler[key.D]:
self.x += self.speed
if self.key_handler[key.W]:
self.y += self.speed
self.update_pos(self.x, self.y)
Before you say I didn't show how the ground is made that is because I can explain this by just saying it is made using a batch draw and all sprite cornets are centered to the sprite. Alright, when I run this code the player falls indefinitely but I want it to stop when there is a tile under it.
If you need to see the batch for the tile I will but I don't know if that is needed and if you see anything unprofessional about how this is written I am always willing to learn how to write more professionally.
This was a stupid question. Just need to make a simplified positioning system based on the blocks, not sprites.

How do i instance sprites in Pygame? Do i need too?

So im working on a game for some coursework in my computing course, im almost finished but for the life of me i cant get multiple of the Spider sprite to spawn in at the correct locations or reset properly between levels. I've tried adding different instances to groups before but i always get a different error with each method that i try. the code is below and im fairly new to pygame so sorry for the messy code..
#
class Spider(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load( 'Assets/Level-Assets/Level 1/Enemies/Spider_Down.png')
self.rect = self.image.get_rect()
self.Health = 3
self.Spawned = False
self.range = 100
self.Gravity= 6
self.pos_difference = 0
self.Active = False
self.Fall = False
self.Spiders = []
##################################################################################################################
def Set_Position(self):
if self.Spawned == False:
self.rect.center = (World.mapx, World.mapy)
Enemies.add(self)
self.Spawned = True
else:
self.rect.center = self.rect.center
######################################################################################################################
def update(self):
self.pos_difference = Player.rect.centery - self.rect.centery
if Player.rect.centerx >= self.rect.centerx -4 or Player.rect.centerx >= self.rect.centerx + 4:
if Player.rect.centery > self.rect.centery:
if self.pos_difference < 200:
self.Active = True
self.Fall = True
if self.Active == True:
if self.Fall == True:
self.rect.centery += self.Gravity
This is the code for the Spider, its not actually doing anything until it is called within the World class, which is where i believe the majority of the problem is...
def DisplayWorld(self):
self.MapLoad = False
for Row in range(len(self.newMap)):
for Col in range(len(self.newMap[Row])):
self.mapx = Col * 64
self.mapy = ((Row + self.Height_modifier) * 64)
self.tile_pos = (self.mapx, self.mapy )
if int(self.newMap[Row][Col]) == 1:
self.rect = self.Brick_Center.get_rect(left = (self.mapx) , bottom = (self.mapy))
self.World_sprites.add(World)
self.Camera_Pos_Check()
Player.Collision_Check()
Spider.Collision_Check()
Shoot.Collision_Check()
self.World_sprites.draw(screen)
elif int(self.newMap[Row][Col]) == 2:
Enemies.add(Spider(screen))
def main():
play = True
while play:
if pygame.key.get_pressed()[pygame.K_ESCAPE]:
play = False
if not Sprites.sprites():
Sprites.add(Player,Spider)
print(Sprites)
clock.tick(CLOCKRATE)
pygame.mouse.set_visible(False)
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
screen.blit(bgi,(0,0))
screen.blit(bgi,(0,500))
World.findLevel()
Sprites.update()
Enemies.draw(screen)
Sprites.draw(screen)
if Shoot.bullet == True:
Shoot.update()
for b in range(len(Shoot.bullets)):
screen.blit(Shoot.image, (Shoot.bullets[b][0],Shoot.bullets[b][1]))
UI.Health_Display()
pygame.display.flip()
Sprites = pygame.sprite.Group()
Enemies = pygame.sprite.Group()
UI = User_Interface()
World = World()
Player = Proto()
Shoot = Shoot()
Portal = Portals()
Spider = Spider()
main()
I've found your problem: you overwrite your Spider class by an instance of it (Spider()) and then give it the same name. Thus you're consistently adding the same single spider to your enemies list. Instead, you should remove this definition on the bottom of your file and where ever you're adding the (multiple) spider(s), you should create this instance.
In a more general remark, it is considered bad style (and not too great for performance) to use global variables as widely as you do. It'd be much better to pass them around between functions and classes. Also, the CamelCase capitalization you use for all of your variables is commonly only used for classes. I'd recommend checking up on pep8 to learn more about how to use common Python styles. It makes these kinds of problems easier to spot, less likely to occur, and simplifies the reading for anyone involved. Using these points properly might even improve your grade significantly ;).

Pygame spritecollide hitboxes

I am working on a simple 2D platformer, but I am having some trouble with the hitboxes of my sprites. I use the pygame.sprite.spritecollide function to generate a list of blocks(platforms) touching my player sprite.
Here is my player class:
class Player( pygame.sprite.Sprite ):
def __init__(self,x,y,image):
super( Player, self ).__init__()
self.vel_x = 0
self.vel_y = 0
self.moveSpeed = 3
self.jumpPower = 7
self.direction = idle
self.grounded = False
self.falling = True
self.jumping = False
self.climbing = False
self.image = pygame.Surface((50,50))#pygame.transform.scale( image, (50,50))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.width = 50
self.height = 50
self.rect.bottom = self.rect.y + self.height
def set_position( self,x,y ):
self.rect.x = x
self.rect.y = y
def experience_gravity(self, gravity = 0.3):
if not self.grounded:
self.vel_y += gravity
self.falling = True
else:
self.vel_y = 0
self.grounded = True
self.falling = False
def jump(self):
self.grounded = False
self.vel_y -= self.jumpPower
def update(self, collidable = pygame.sprite.Group() ):
global collision
self.experience_gravity()
self.rect.x += self.vel_x
self.rect.y += self.vel_y
collision_list = pygame.sprite.spritecollide( self, collidable, False )
for p in collision_list:
if ( self.vel_y > 0 ):
self.vel_y = 0
self.grounded = True
In my the update method, I check for collisions between a sprite group holding all of my platforms (parameter collidable) and the player.
Here's my block class:
class Block( pygame.sprite.Sprite ):
def __init__( self, x, y, width, height):
super( Block, self ).__init__()
self.image = pygame.Surface( ( width, height ) )
self.image.fill( black )
self.rect = self.image.get_rect()
self.type = platform
self.rect.x = x
self.rect.y = y
self.width = width
self.height = height
Pretty self-explanatory block class. The rest of my code is to update and blit everything onto a white background. The problem I run into is that when the player lands on a platform, it only stops falling (becomes grounded) when it is already in the platform. Even stranger, the depth the block sinks intot he platform is not consistent. Sometimes it will fall in 10 pixels, other ties 20 pixels. Here's a screenshot of the player getting stuck:
The player block is stuck inside the platform block
So this is really baffling me, especially since the amount the block falls in is inconsistent. If anyone could give me an idea on how to fix this, I'll really appreciate it.
With kindest regards,
Derek
It's hard to diagnose without any debugging trace, but I have an idea or two.
First of all, dump a debugging trace: print the salient values at each time step: position of the player block, positions of colliding blocks, and applicable velocities. This should give you a table-like snapshot at each time step of the involved objects.
Now, look at the values in the last time step before the collision. I suspect that you might have something like a player velocity of -20, but landing on a platform only 10 pixels below. Your update logic needs to handle the landing between the two time steps, and stop the player at the platform height. It looks to me as if the player is allowed to fall for the entire time step, which will embed his tender lower extremities 10 pixels into the platform.
Second, check that you're comparing the nearer edges: in this case, the player's lower edge with the platform's upper edge. You will later need to handle the case where a player's lower-right corner contacts the upper-left corner of a block, and you have to determine whether the player first hits the block's top (landing) or side (change horizontal velocity and drop).
I wouldn't alter the computed velocity; I would think it's easier just to adjust the final position. The last block of code you posted is where you already deal with a collision as a special case. When you detect a collision, get the top surface (y-value) of the object. If that's above the current position of the bottom of the player, assign that top surface as the correct position. This will leave your player sitting on top of whatever is the highest object on the collision list.
Note that this works only as long as your entire collision list is from falling; for lateral collisions as well, you'll need to separate the lists and adjust each boundary as needed. This can get complicated if there are collisions from several sides on the same time step -- for instance, a goombah slams into the player from the left just as the player hits both a wall and a platform.

Categories