I am trying to get a good collision with my rectangles but I feel like my method is bad because when ever I job and collide with my other platform my player keeps getting stuck on it, is there a way I could make it collide good without it getting stuck I am just looking for a good collision Thank You!
VIDEO < as you can see my player keeps getting stuck on the platform its the same thing for left and right when I collide with the platform it will just make my player stuck without proper collision
# collisions
for platform in platforms:
if playerman.rect.colliderect(platform.rect):
collide = True
playerman.isJump = False
if (platform.rect.collidepoint(playerman.rect.right, playerman.rect.bottom) or
platform.rect.collidepoint(playerman.rect.left, playerman.rect.bottom)):
playerman.y = platform.rect.top - playerman.height + 1
playerman.moveright = True
playerman.moveleft = True
if (platform.rect.collidepoint(playerman.rect.right, playerman.rect.top) or
platform.rect.collidepoint(playerman.rect.right, playerman.rect.bottom - 10)):
playerman.moveright = False
elif (platform.rect.collidepoint(playerman.rect.left, playerman.rect.top) or
platform.rect.collidepoint(playerman.rect.left, playerman.rect.bottom - 10)):
playerman.moveleft = False
else:
playerman.moveright = True
playerman.moveleft = True
my full code: script
I been searching everywhere for proper collision and I cant manage to find any good working ones
From your video it looks like the collision detection does not work if you come up from below a block.
In general (see below for an example): I encountered the same "puzzle" with my game and as far as I could see there are two possible ways.
Pre-Checking
You check how far away the player is from the closest "block" and let the player move only so far. This includes:
checking which blocks are the closest to the player
checking the distance to each close block and calculating the remaining possible x pixels the player can move in direction z.
move player exactly x pixels in direction z.
Post-Checking (I used this as it was easier to figure out on my own back then)
You move the player according to his current speed and then check for collisions. If you get a collision, you move the player back for the amount of pixels which is the intersection between player-border and block-border.
move player according to his speed to the full extent
check collisions for all close blocks (or all blocks on the map for starters and you can improve the performance from there)
if you get a collision, calculate by how much the player intersects with the colliding block. This is easy if your player's hitbox is a rectangle and you work with a tilemap made up of rectangular blocks, you can simply subtract player.x and block.x coordinates
move the player back (before updating the screen) by that amount of pixels.
If you want to learn more about it and have in-depth code examples (if you don't want to try and error by yourself until you figure it out) I suggest searching on youtube for pygame-2D collision detections, there are great teachers on there.
Here is an excerpt of my collisiondetection_x_axis() method (self references the player!)
# move player etc ...
for tile in map_tiles: # for all tiles on the map
if pygame.sprite.collide_rect(self.hitboxBody, tile):
if self.SpeedX < 0 and tile.rect.right > self.hitboxBody.rect.left: # moving left and collision
# move char back to "in front of the wall"
self.rect.x += tile.rect.right - self.hitboxBody.rect.left
self.SpeedX = 0 # set speedX to zero, as we cannot move in that direction anymore
elif self.SpeedX > 0 and self.hitboxBody.rect.right > tile.rect.left: # moving right and collision
# move char back to "in front of the wall"
self.rect.x -= self.hitboxBody.rect.right - tile.rect.left
self.SpeedX = 0 # set speedX to zero, as we cannot move in that direction anymore
collision_detection_y_axis:
for tile in map_tiles: # for all tiles on the map
if pygame.sprite.collide_rect(self.hitboxBody, tile):
if self.SpeedY < 0 and tile.rect.bottom > self.hitboxBody.rect.top: # moving up
self.rect.y += tile.rect.bottom - self.hitboxBody.rect.top # move char back to "below the wall"
self.SpeedY = 0
elif self.SpeedY > 0 and self.hitboxBody.rect.bottom > tile.rect.top: # moving downwards
self.rect.y -= self.hitboxBody.rect.bottom - tile.rect.top # move back to "on top of the wall"
self.SpeedY = 0
self.jumping = False # on ground
Edit: this requires your movement between collision-checks to be less than the width of a block, otherwise your character can 'glitch' through blocks if he has enough speed.
Note: you should take into the account the direction your player is moving before you do collision-tests, it makes it easier to determine which side of the player will possibly collide first with a block. For instance if you are moving to the right, then the right side of the player will collide with the left side of a block. Then write a collision detection for those two points as well as consequent action (e.g. reset to a position in front of the block and speed_x = 0)
PS: Try and use the function pygame.Rect.colliderect, it tests if two rects overlap (=collision), I have a feeling the way you set up your collidepoint-functions don't return collisions for all possible scenarios.
Related
I am using ursina game engine however this is mainly about the math so I don't think the engine matters.
In the engine there is no such thing as "front" so if I want a player to move front I have to use this mathematical formula:
player_x += player_x *cos(degrees)
player_z += player_z *cos(degrees)
This works for everything I throw at it, except for a weapon I am trying to put into the game. I want it to follow the player, which it does, but when it rotates, it rotates based on its own center, so the gun doesn't follow the player.
How can I make it so that the rotation of the weapon follows the player?
I can't just make it like:
gun.rotation_y = player.rotation_y
gun.x = player.x
gun.z = player.z
because then it would be in the player, and if I move it forward, it will rotate on its own center, so it wouldn't be aligned with the gun.
You can try to make smth like
gun.rotation_y = player.rotation_y
gun.x = player.x + (and put value here so it near player e.g 10)
gun.z = player.z + (and put value here so it near player e.g 10)
Parent the gun to the player with gun.parent = player and position it how you want. It will then move along with the player.
For the forward movement you can just do player.position += player.forward * held_keys['w'].
I've only seen information about collisions with walls on here but I'm having a problem making the player "bump" its head on the ceiling. Instead of hitting its head, velocity being set to 0 and it accelerating back down due to gravity, the object gets sucked up ontop of the platform above it. Why isn't this working and how can I fix it? I'm following a tutorial and trailing off to my own adaptation and the content creator said that it would be hard to implement. Apparently there may also be an error if you collide diagonally? (according to the content creator)
Thank you so much in advance, teacher wants me to create something to present infront of people in only a couple of days with no experience with any sort of game engine.
def update(self):
# Game Loop - Update
self.all_sprites.update()
# check if player hits a platform
hits = pygame.sprite.spritecollide(self.player,self.platforms , False)
if self.player.vel.y > 0:
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
if self.player.vel.y < 0:
if hits:
self.player.rect.top = hits[0].rect.bottom
self.player.vel.y = 0
Instead setting the velocity to 0, you might want to set the velocity to -1 or some other negative value to force movement 'downward' out of the boundary condition. You could also add acceleration if desired in the same condition:
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = -1 # example velocity
self.player.acc.y = 2 # example acceleration
Is it possible for me to create a function where it displays a message if the Sprite (Rocket) collides with the astroid objects?
class Rocket(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect=self.image.get_rect()
self.image=Rocket.image
self.firecountdown = 0
def setup(self):
self.rect.x=700
self.rect.y=random.randint(20,380)
def updateposition(self):
self.rect.x=self.rect.x-1
time.sleep(0.005)
if self.rect.x == 0 :
self.rect.x = 700 + random.randint(0, 100)
self.rect.y=random.randint(20,380)
asteroids=[]
asteroidsize=[]
for i in range(25):
x=random.randrange(700,10000)
y=random.randrange(0,400)
asteroids.append([x,y])
asteroids[i]=Asteroid()
for i in range(25):
asteroidsize.append(random.randint(6,15))
while True:
for i in range(len(asteroids)):
pygame.draw.circle(screen,GREY,asteroids[i],asteroidsize[i])
asteroids[i][0]-=2
if asteroids[i][0]<0:
y=random.randrange(0,400)
asteroids[i][1]=y
x=random.randrange(700,720)
asteroids[i][0]=x
You could write a function on your Rocket class that checks for collisions. Since the asteroids are circles, you'll want to check if the closest point on the circle to the center of your sprite's rect is within the rect's bounds:
def check_asteroid_collision( self, asteroid, size ) :
# Create a vector based on the distance between the two points
distx = self.rect.centerx - asteroid[0];
disty = self.rect.centery - asteroid[1];
# Get magnitude (sqrt of x^2 + y^2)
distmag = ((distx * distx) + (disty * disty)) ** 0.5;
# Get the closest point on the circle:
# Circle center + normalized vector * radius
clsx = asteroid[0] + distx / distmag * size;
clsy = asteroid[1] + disty / distmag * size;
# Check if it's within our rect
if self.rect.collidepoint( clsx, clsy ) :
# We're colliding!! Do whatever
print( "Oh no!" );
Then in your game loop, you could check collisions by calling this:
while True:
for i in range(len(asteroids)):
...
# Collision checking
myrocket.check_asteroid_collision( asteroids[i], asteroidsize[i] );
Keep in mind this process is somewhat expensive and it will check every asteroid if it's colliding. If there's a large number of asteroids, it'll run slowly.
While I dont code python I can give you a simple example of how to accomplish something like this.
Make all your game objects inherit from a general game item class, this way you know all items have a position and a collision radius.
class:
int positionX
int positionY
int radius
Then keep all your items in a global list of game objects.
Loop over your game list and see if any two items collide
foreach object1 in gameObjectsList:
foreach object2 in gameObjectsList:
if(object1 != object2)
if(math.sqrt(object1.positionX - object2.positionX)**2 +
(object1.positionY - object2.positionY)**2)
<= object1.radius + object2.radius.
//Things are colliding
As the game progresses make sure you keep the position variables updated in each object.
What this means is basically that you have your list of game objects, and you loop over these every game frame and check if any of them are touching each other.
Or in terms of pure math, use the distance formula (link) to get the distance between the two items, and then check if their combined radius is greater than this distance. If it is they are touching.
Yes, making the score is possible. I am asuming you know the sprite collision function : pygame.sprite.spritecollide(). If you don't, look into the PyGame Docs. or google it. But here is how you do it. First. add these lines of code at the beginning of your code after the pygame,init() line:
variable = 0
variable_font = pygame.font.Font(None, 50)
variable_surf = variable_font.render(str(variable), 1, (0, 0, 0))
variable_pos = [10, 10]
Clearly, variable can be a string, just remove the str() in line 3. Line 1 is self-explanatory - it is the value you will see on the screen (just the stuff after the = sign and/or the parantheses). Line 2 decides what font and size you want the message to be in. If you choose None and 50, it means that you want the message in a normal font and in size 50. The third line renders, or pastes, the message on the screen the name of the variable that contains the string/number, the number 1 (I have no idea why), and the color your message will be. If the variable contains a number, put a str() around it. The last line will be the position of the message. But you will need to blit it first. To prevent the message from appearing on the screen forever, make a variable:
crashed = 0
Then make your instances and groups:
ship = Rocket(None)
asteroids = pygame.sprite.Group() #This is recommended, try making a class for the asteroids
And finally your collision code:
if pygame.sprite.spritecollide(Rocket, asteroids, True):
crashed = 1
You can make your blits controlled with the crashed variable:
if crashed == 0:
screen.blit(ship.image, ship.rect)
elif crashed == 1:
screen.blit(ship.image, ship.rect)
screen.blit(variable_surf, variable_pos)
The last blit line will blit your message on the screen at the location listed (variable_pos) when your ship crashes (crashed = 1 in the collision code). You can use make some code to make crashed back to 0. Remember to do pygame.display.flip() or weird stuff will happen. I hope this answer helps you!
I am currently working on creating a game using python 3.2.3 and pygame. I am creating a game that is very similar to Mario (a side scroller); my game involves a series of 'bricks' that the character will need to jump on and be able to collide with. I have created a level in photoshop and mask that is colour coded so that red is the area that the character will die, green is the colour for the bricks, and blue is the colour of the ground. Here is the code I am having a problem with:
guy[y]+=guy[vy] # add current speed to Y
col = mask.get_at((int(guy[x]+662), int(guy[y]+59)))
if col == (0,0,255,255):
guy[y] = 537
guy[vy] = 0
guy[onground] = True
if col == (0,255,0,255):
guy[vy] = 0
guy[onground]=True
elif col == (255,0,0,255):
guy[vy] = 5
guy[onground] = False
if guy[y]+59 >= 720:
lives -= 1
screen.blit(guyPic,(640,guy[y]))
guy[vy]+=.75
When I try to make the character land on a brick (or on the green), he lands but he begins to sink. He can also walk right through bricks. I would like the character to be able to collide with the brick so that he cannot walk right through them, but he is able to jump and land on them without sinking through. I do know another method that is possible which is to draw rects at the coordinates of each brick but this method would be very inefficient as it would be too much code. I would greatly appreciated some help with this error as I have tried to fix it for the past two weeks. Any help is appreciated.
Drawing rects is better. And it's logically easier. You can just detect if the two rects collides, because pygame has already supported collide detection. You can see this:pygame.Rect.colliderect
Make sure you put the 'guy[vy]+=.75' is at the start of the code. Before despite what the collisions set the y speed too, it was always being set back to .75 at the end of the code. therefore when you hit the ground you would still move through it at a speed of .75 y. Also try limiting the maximum fall speed by adding a conditional statement for the 'guy[vy]+=.75' so it only runs 'if guy[y] <= 10:'
Good Luck :).
guy[y]+=guy[vy] # add current speed to Y
guy[vy]+=.75
col = mask.get_at((int(guy[x]+662), int(guy[y]+59)))
if col == (0,0,255,255):
guy[y] = 537
guy[vy] = 0
guy[onground] = True
if col == (0,255,0,255):
guy[vy] = 0
guy[onground]=True
elif col == (255,0,0,255):
guy[vy] = 5
guy[onground] = False
if guy[y]+59 >= 720:
lives -= 1
screen.blit(guyPic,(640,guy[y]))
I'm essentially trying to make a "solid" object with pygame. The goal is to repel the player when they come in contact. What I'm currently using (but doesn't work correctly) is the following:
keys_pressed = pygame.key.get_pressed()
if 1 in keys_pressed:
if keys_pressed[K_w]:
self.player_l[1] += -2
if self.player_r.colliderect(self.tower_r): self.player_l[1] -= -2
if keys_pressed[K_a]:
self.player_l[0] += -2
if self.player_r.colliderect(self.tower_r): self.player_l[0] -= -2
if keys_pressed[K_s]:
self.player_l[1] += 2
if self.player_r.colliderect(self.tower_r): self.player_l[1] -= 2
if keys_pressed[K_d]:
self.player_l[0] += 2
if self.player_r.colliderect(self.tower_r): self.player_l[0] -= 2
The problem with this is that the player gets "stuck" inside the tower Rect, despite returning to a location where they were before the collision is initiated, the player Rect will always be pulled back in to the tower, and the collision will continue to trigger. After initially touching the tower Rect, the player will be unable to move in any direction.
I have done the same thing in a pygame game of mine. What you want to do is make a function for moving that all objects will use. It makes it impossible to go through any sprite in a render updates group called everything. If a sprite is not part of everything, it will not collide. Here is the function. This creates a resistance of a certain amount for collisions. Basically, upon pushing on an object, it will push a certain amount back. Any object that doesn't call the move function will not move even if it is pushed upon, so only objects that can move in the first place can be pushed, while things like walls will not slide across the board when you push them.
def moveRelative(self,other,speed): #This function is a function the one you need uses, which you may find useful. It is designed to move towards or a way from another sprite. Other is the other sprite, speed is an integer, where a negative value specifies moving away from the sprite, which is how many pixels it will move away from the target. This returns coordinates for the move_ip function to move to or away from the sprite, as a tuple
dx = other.rect.x - self.rect.x
dy = other.rect.y - self.rect.y
if abs(dx) > abs(dy):
# other is farther away in x than in y
if dx > 0:
return (+speed,0)
else:
return (-speed,0)
else:
if dy > 0:
return (0,+speed)
else:
return (0,-speed)
def move(self,dx,dy):
screen.fill((COLOR),self.rect) #covers over the sprite's rectangle with the background color, a constant in the program
collisions = pygame.sprite.spritecollide(self, everything, False)
for other in collisions:
if other != self:
(awayDx,awayDy) = self.moveRelative(other,-1) #moves away from the object it is colliding with
dx = dx + 9*(awayDx) #the number 9 here represents the object's resistance. When you push on an object, it will push with a force of nine back. If you make it too low, players can walk right through other objects. If you make it too high, players will bounce back from other objects violently upon contact. In this, if a player moves in a direction faster than a speed of nine, they will push through the other object (or simply push the other object back if they are also in motion)
dy = dy + 9*(awayDy)
self.rect.move_ip(dx,dy) #this finally implements the movement, with the new calculations being used
it is kind of a lot of code an you may want to change it for your purposes, but this is a pretty good way to do it. If you want to eliminate the bounce back feature, you could consider just setting any movement towards the object to zero, and allowing movement away from it only. I have found the bounce back feature useful and more accurate for my game, however.