I'm using a game engine to make my own 3D game in python. I need to simulate a jump and/or gravity, but I'm running into an issue : either the calcul is immediate and my character isn't moving, like, there's no animation, instant blink, or either (if for exemple I had a bigger number in my for loop) it takes wayyyy more time, really slow, it lags and everything, struggling to calculate the jump. I got both extremes, and none works, so I'd like to find a way to make my jump. All I have at my disposition to do this is :
player.y +=
#can be -=, =, +=, etc.
So, do you got any ideas of ways to do this ? I'm not really asking a specific problem, I'd just like to gather some ideas ! And there's even no need to give predone examples, just throw your ideas, like : use this, try with this formula, this function, etc.
Adding some details : what I already tried.
Here is the main solution I tried, pretty basic :
velocity = 3
def input(key):
global velocity
if key == "space":
for i in range(7):
print(velocity)
player.y += velocity
velocity -= 1
velocity = 3
Which is pretty cool, as you had to your height 3, then 2, then 1 (deceleration as your energy lowers), then you add -1, -2, -3 (acceleration due to gravity), and go back to your starting point. Perfect ! But, as said, instantly done. So if I try this :
velocity = 3
def input(key):
global velocity
if key == "space":
for i in range(61):
print(velocity)
player.y += velocity
velocity -= 0.1
velocity = 3
Again, instantly done. And if I try higher and higher intervals, at some point I just get it to lag, no in-between where it's done correctly
Slightly off-topic: You don't want to name your function input() because it shadows the inbuilt input() function.
The problem is that you change the velocity and then iteratively decrement it inside a loop! Because of the way python (or most programming languages, for that matter) works, the program execution moves on to the "draw on screen" step only after it's finished executing your input() function. So when you press a key, here's what your program is doing:
Draw a frame and listen for keypress
Key pressed! Call input() to handle the keypress (let's assume player.y = 0)
Is the key a space? Enter the loop
velocity = 3. Move player up by 3. Decrement velocity. player.y = 3
velocity = 2. Move player up by 2. Decrement velocity. player.y = 5
... and so on until you exit the loop
player.y is 0 again
Draw another frame. Player is at the same place they started, so it looks like nothing happened.
When you add iterations to your loop, this process takes longer (so you see lag), but essentially the same thing happens.
To fix this, you need to add the effect of gravity inside the function that draws your frames. For example, you could set a flag when the jump key is pressed, and if you had a function step() that was called at each timestep of your simulation, you could check if the flag is set and then handle the situation
def user_input(key):
global jump_velocity, is_player_jumping
if key == "space":
is_player_jumping = True
jump_velocity = 3
def step():
global jump_velocity, is_player_jumping
if is_player_jumping:
player.y += jump_velocity
jump_velocity -= 0.1
if player.y == 0: # Player is back on the ground
is_player_jumping = False
This way, you only change the player's location a little bit before the next frame is drawn, and you can actually see the animation.
You first need to know what is the current time step because if you have 2 ms between your frames and 20 ms your need to adapt the amount you get into the player's y position each step.
then it would be great to have a player velocity variable somewhere in addition to its position. Then you would have to decide on a velocity to add instantly to the player when the jump occurs and each time step adds a bit of acceleration down due to gravity.
I have been trying to figure out a way to check each adjacent cell for my minesweeper game and am coming up short. I am a beginner in python and would also like to start using OOP. however before I can even get there, I need to rectify this. all the tutorials I have seen don't use basic python, but different versions to the IDLE i use, so I am struggling. can anyone help me? I need to be able to go around each adjacent cell and check if there is a bomb there. The value to check if there is a bomb there is 1 and will also turn red. Thank you all so much!
also if you could dumb it down a little for me, that would be lovely.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
First up, you definitely want to make an OOP project out of this. Minesweeper's probably near the complexity limit of what you can reasonably do without object-oriented programming, but if you want to take the basic Minesweeper concept and make it more interesting / complex, you're going to need better structure.
And even if you're not considering making it more complex, thinking about what sorts of complexities you could add in is helpful in planning your classes. (Perhaps you didn't realize there's a "planning your classes" step? I'll get to that.) My steps here should work for pretty much any beginning OOP project, since it seems Minesweeper's just a convenient example.
I realize this answer will be a giant diversion from OP's how-do-I-check-the-nearest-neighbors question, but OP was also asking about OOP and answering their question in an OOP context means getting a class model in place. Trying to retrofit OOP onto non-OOP code is possible, but is usually harder than doing OOP from scratch.
If you're new to OOP in Python, check these two tutorials. Both of them run through the basics, neither requires an IDE, and neither introduces complexity / features beyond what you need to start. The rest of my answer will assume you're familiar with how to write classes. I highly recommend typing out both tutorials on your own terminal and trying a couple variants on them before you go further.
Now that you know roughly what a class is, make a list of all the operations you're going to want in your Minesweeper program, and what sorts of things come up in it. The point here isn't to write code, and natural language will serve as pseudocode here. Things you'll probably want to include: "Add a mine to a cell on the grid," "check a cell on the grid," "display the number of spots near a given cell on the grid." You might also include some "future plans" like "resize the grid in the middle of a game", "limit number of moves," "get hint," "allow mines to move," "have people that move through minefield," multiple types of checks / mines / cells, hex grid as an alternative to a square grid, strangely-shaped and 3D grids, etc. Don't worry if you don't know how to code those things yet. If you've got an interesting idea, include it on the list.
Go through that list and make some notes about the main words / concepts that keep coming up. These will be your classes. For Minesweeper, I'd list "Cell," "Grid," "Game," "Check," and "Mine," but your mileage may vary. These are not necessarily the final class list. They're just a step toward organizing your thoughts so you can turn the high-level "Minesweeper game" into something concrete & extensible. You can add / remove classes later once you get a better feel for which ones you'll actually use.
Note the relationships between objects of the classes, including which ones need to be described for the others to make sense / be instantiated. This step is analogous to how you might plan a relational database: A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern, a Cell may or may not contain a single Mine, a Check reveals whether a Mine is in a given Cell and if not, reveals the number of Mines in Cells adjacent to the Checked Cell, etc.
Open up your IDE, and start blocking out classes in a file. The point here isn't so much to write code as to get all your classes and notes into the code for easy reference. If this file starts to get big, think about how the classes group together and split that one file into several, with import statements connecting them. By "blocking out classes," I mean "writing the simplest classes which will compile, but don't try to make them run." At this stage, you can / should have classes that look like this:
class Grid:
"""A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern"""
def __init__(self, game, **setup_params):
"""Initialize the grid given a game and possibly other parameters for shape and size"""
raise NotImplementedError('This code has not yet been written.')
def resize_grid(self, new_size):
raise NotImplementedError('This code has not yet been written.')
def check_cell_at(self, position):
raise NotImplementedError('This code has not yet been written.')
Things to check: This is completely legal Python and compiles fine. All of your notes from steps 2-4 should end up in docstrings. All of the target functionality you described in your notes corresponds to particular methods on classes that have something to do with those functions. Every class you've described is present, and has a docstring describing its purpose & structure. Every class's __init__() method takes the arguments that are necessary to instantiate the class. Every method takes some parameters that will likely be helpful. You can always edit the parameter lists later, but again, the point is to organize your code before you get too far into writing it, so that relationships and functionality are easy to track. If you're using Git or another version-tracking tool, make your first commit as soon as you're done with this step.
Now that you've got all the structure blocked out, figure out what the "main entry point" is going to be, instantiate that class (probably Game in my approach), and check that your code compiles, runs, and exits with a NotImplementedError coming with the line and message you expect. (If so, that's success!) If the project feels big, or if you're going to be sharing code with other developers, you may also want to add unit tests at this stage. The expectation is that every test will fail, but writing the tests helps to document the planned functionality.
Fill in the methods one by one, add unit tests to go with new or updated methods, and try running the code a bit at a time. You don't have to write the methods in any particular order, but it will be easier to test once all the __init__() methods are complete, and those are generally straightforward.
Now, how do you check the neighbors of a cell?
You should have methods on a Cell for "get list of my neighbors," and "get whether this cell contains a mine." You probably also have a method on Grid to "get Cell at position." You might have multiple types of Checks, but the base Minesweeper game only has the one, so Cell has a method like
def check(self):
"""Returns "BOMB" if this cell has a Bomb. Otherwise, returns the number of neighbors with bombs as an integer."""
if self.has_bomb:
return "BOMB"
neighboring_mines = 0
for cell in self.grid.get_neighbors_of(self.position):
if cell.has_bomb:
neighboring_mines += 1
return neighboring_mines
There are only 8([1,1],[1,0],[1,-1],[0,1],[0,-1],[-1,1],[-1,0],[-1,-1] relative to the clicked grid coordinate) adjacent squares, so it should be easy to do what you are asking only with "if" statements
anyway, just check the surrounding squares and if true add to a variable.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
#print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
print(grid)
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
NBombs = 0
for i in range(-1,2):
for k in range(-1,2):
if (i!=0 or k!=0) and (row+i>=0) and (column+k>=0) and (row+i<len(grid)) and (column+k<len(grid[0])):#prevents from both being 0, or for the index to be negative or out of range
print(i,k)
if grid[row+i][column+k] == 1:
NBombs+=1
print("Number of bombs:",NBombs)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
So basically like another answer in this thread, you can check the surrounding/adjacent tiles by adding -1, 0, or 1 to both your current x and y.
You could write a separate function that would return a list of the coordinates of the surrounding tiles with bombs and take the tile coordinates and grid as arguments like the following:
def check_surrounding_tiles(current_x, current_y, grid):
bomb_tiles = []
#Check starting from position (current_x - 1, current_y - 1)
for i in range(-1, 2):
for j in range(-1, 2):
#Ignore the current x and y, and do bounds checking
if (i != 0 or j != 0) and (current_x + i) > -1 and (current_y + j) > -1 and (current_x + i) < len(grid) and (current_y + j) < len(grid[0]):
if grid[current_x + i][current_y + j] == 1
bomb_tiles.append[(current_x + i, current_y + j)]
return bomb_tiles
This, like mentioned above, will return a list of coordinate pairs with bombs in them.
If you need to get the number of bombs, simply use the following:
adjacent_bomb_count = len(check_surrounding_tiles(<x position>,<y position>, grid))
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.
I'm working on a simple simulation using Pygame. For start I need to create 20 objects and randomly place those along edges of the game window, excluding the top edge. SingleCell class manages objects and defines randomised starting positions for sprites.
This class is then being called in the main simulation class to create 20 sprites and add them to a group:
def _create_cell(self):
"""Create a single sprite and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
c_width, c_height = c.rect.size
self.cells.add(c)
This all works fine, but quite a few sprites end up overlapping. In order to fix it after studying docs for pygame.sprite I decided to use pygame.sprite.spritecollideany() in a loop to check whether any of the sprites in a group do collide with one another and move them either horizontally or vertically by width or height, respectively, +1 pixel:
def _check_overlapping_cells(self):
"""Check cells group for collisions based on rect"""
for cell in self.cells:
if pygame.sprite.spritecollideany(cell, self.cells,
collided=None) != 'None':
#If the collision happens along the vertical boundary
#move the sprite down by 1 height +1 pixel
if cell.rect.x == 0 or cell.rect.x == (
self.settings.screen_width - cell.rect.width):
cell.rect.y += (cell.rect.height + 1)
#If the collision along horizontal edge then update x-coord
#by sprite width +1 pixel
elif cell.rect.y == 0:
cell.rect.x += (cell.rect.width + 1)
This worked. Sort of. Some of sprites would still be overlapping others in their new locations. So instead of if I've decided to use while cycle to keep moving them around until there are no more collisions:
def _check_overlapping_cells(self):
"""Check cells group for collisions based on rect"""
for cell in self.cells:
while pygame.sprite.spritecollideany(cell, self.cells,
collided=None) != 'None':
Unfortunately, this causes the sim to enter a seemingly neverending cycle of moving sprites around.
I'm a bit confused as to how to do it properly. Any advice?
EDIT:
I have since tried another approach of trying to check collisions when a sprite is being created by modifying _create_cell method so now it looks like this:
def _create_cell(self):
"""Create a single cell and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
c_width, c_height = c.rect.size
if pygame.sprite.spritecollideany(c, self.cells,
collided=None) != 'None':
#If the collision happens along the vertical boundary
#move the sprite up by 1 height +1 pixel
if c.rect.x == 0 or c.rect.x == (
self.settings.screen_width - c.rect.width):
c.rect.y += (-c.rect.height - 1)
self.cells.add(c)
#If the collision along horizontal edge then update x-coord
#by sprite width +1 pixel
elif c.rect.y == (self.settings.screen_height - c.rect.height):
c.rect.x += (c.rect.width + 1)
self.cells.add(c)
elif pygame.sprite.spritecollideany(c, self.cells,
collided=None) == 'None':
self.cells.add(c)
But this way results in fewer than 20 sprites being created and some are still overlapping for some reason.
Perhaps something like this:
def _create_cell(self):
"""Create a single cell and add it to group"""
for cell in range(0,self.settings.cell_count):
c = SingleCell(self)
while spritecollideany(c, self.cells):
c = SingleCell(self)
c_width, c_height = c.rect.size
self.cells.add(c)
Basically, the while loop keeps generating new Cells until it finds one that doesn't collide with any in self.cells. Of course, it that is not possible, then it will loop forever. You could add a counter and abort if it tries too many times.
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.