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))
Related
So the question is simple:
Given a Surface, let's call it screen and x,y coordinates, can I get anything that lays at that coordinates on that Surface?
For example, let's say we have typical, Player attack, and if the attack reach the Enemy position x,y then enemy dies.
So given this simple app (is an example only not a real app)
import pygame as pg
from pygame.math import Vector2
# pygame constants
CLOCK = pg.time.Clock()
WIN_SIZE = (1280, 640)
# pygame setup
pg.init()
# screen
window = pg.display.set_mode(WIN_SIZE, 0, 32)
background = pg.Surface(WIN_SIZE)
player = pg.Surface(Vector2(12, 64))
player_rect = player.get_rect(topleft=Vector2(150, 150))
player_attack = False
player.fill((102, 255, 178))
player_attack_range = 20 # player can hit at min 20 pixel from target
enemy = pg.Surface(Vector2(12, 64))
enemy_rect = player.get_rect(topleft=Vector2(175, 150))
enemy.fill(pg.Color("green"))
while True:
background.fill((0, 0, 0)) # screen clear
# Render enemy
attacked = False
if player_attack:
# !!!!! HERE !!!!!
# Now we check if the playuer is close enough to the enemy, so we MUST know the enemy pos
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
if not attacked:
enemy.fill(pg.Color("green"))
background.blit(enemy, enemy_rect.topleft)
# Render player
background.blit(player, player_rect.topleft)
# Events
for event in pg.event.get():
if event.type == pg.QUIT or (
event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE): # x button and esc terminates the game!
exit(1)
# ............. Mouse ............. #
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
player_attack = True
if event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
player_attack = False
pg.display.update() # 2) Update the game
window.blit(background, (0, 0)) # 3) Repaint the screen
CLOCK.tick(60) # 4) Wait 60 Frames
When is attacked
Now I always seen it done this way more or less:
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
With this example, I'm not pointing out the code implementation but the fact that, the player must know the target position and then check whether or not the target is hit
But what I want to know, let's say I don't know the enemy position, and the player just attacks, is there a way that we can get what's currently on the surface at the attack range?
So do something like
attacked_area_x = abs(player_rect.x + player_attack_range) # only care of x coords
rects_or_surfaces_in_area = background.what_have_we_got_here(Vector(attacked_area, 0))
for r in rects_or_surfaces_in_area:
print("Hit!")
Update
So By checking MDN documentation of Game Development MDN I actually find a game algorithm / Technique that is similar (but concept is the same) of my solution.
Is called the Broad Phase
From the documentation:
road phase should give you a list of entities that could be colliding. This can be implemented with a spacial data structure that will give you a rough idea of where the entity exists and what exist around it. Some examples of spacial data structures are Quad Trees, R-Trees or a Spacial Hashmap.
So yes, it seems one of many good approach to solve this problem.
So, after some research and thanks to Rabbid76 and his answer here How do I detect collision in pygame? which covers in details the most common collisions in Pygame, it seems that what I was looking for natively is just not possible.
Maybe is normal, I'm also new to game development and maybe what I want to do just doesn't make any sense, but I bet it does.
The scenario I'm facing is, just one player with a sword hitting, so I asked my self, why should I need to know prior what objects lie on the sword path and check if is hit, instead, do the hit and request to the parent screen "what object are in the sword path"? , which, is for sure faster because we don't need to know what object that have collision are (and avoid a for loop and check for each react/surface).
So, let's say there are many many object that have collision and a player may hit it, it would be way faster do don't know what' there but request it instead to the surface, so basically the surface should be aware of its children / objects.
I tried a bit the Surface.subsurface() and the Surface.get_parent() but couldn't make it work, anyway still, in a surface area we may have many thinks like:
draws, Rect, Surfaces, Sprites, Imgs etc...
I have only 2 solutions in my mind:
Map the objects coordinates
This only really works if, the entities are static, if so, then we could create a dict with key x:y coordinates and then check if the sword is in within a certain x:y and exist in the dict, then the entity is hit.
But with entity then moves by themself, is a nigtmare and will have a worst problem than before, you would need to update the keys at each frame and so on..
Make it 'distance' sensible
So, this could apply to each entity that is moving and may or may not hit something that has a collision. But staying on the example context, let's say we are iterating thourgh entities / object that at each frame are updating their position.
We could check how distant are from the player, let's say 2 chunks away (or the sword lenght) and collect them in a list
Then we that list, we check if, once the sword is updated, is hitting anything close by.
Still, pretty sure there are better ways (without changing or extending pygame its self).
And maybe, by extending pygame, there may be a way to implement a 'surface aware' class that when we request a specific Rect area, it tells us what's there.
Context: I'm a low intermediate level programmer trying to make a game with my own code in order to improve my ability. I know pygame etc... exists but I wanted to try and make my own engine. I'm currently trying to create a display system. My thought process was to create a 2D list: SURFACE where SURFACE[y][x] is a 'pixel' at coordinates x,y on the python console. This worked great. But I wanted more, and thought: Why don't I create 3 levels of surface, BACKSURFACE, MIDSURFACE, and FRONTSURFACE. My idea was that I could create a single flattened image by combining whatever is rendered through BACKSURFACE, MIDSURFACE, and FRONTSURFACE. This way I could do things like, create an options menu that overlays on top of the game without clearing the entire display.
Now comes my Issue. I created a function addToSurface() that takes a surface, the x, y positions of where to render the image/text, and a list that contains the render item.
def addToSurface(surface, x_pos, y_pos, renderItem):
global SCREENWIDTH, DISPLAYHEIGHT
returnSurface = []
returnSurface = surface.copy() # Make a copy of the surface list
itemHeight = len(renderItem)
itemWidth = len(renderItem[0])
if itemHeight <= DISPLAYHEIGHT and itemWidth <= SCREENWIDTH:
render_y = 0
for y in range(y_pos, itemHeight+y_pos):
render_x = 0
for x in range(x_pos, itemWidth+x_pos):
if renderItem[render_y][render_x] == " ": # Because the 'image' files are ASCII, I turn every
# space in the image into a [None] pixel. The
# original surface lists are initialised where
# every pixel is [None]
returnSurface[y][x] = [None]
else:
returnSurface[y][x] = renderItem[render_y][render_x] # If a pixel in the image isn't empty
# the equivalent pixel coordinate in the
# surface becomes the pixel.
render_x += 1
render_y += 1
return returnSurface # A new version of the surface is returned.
For example, if I run BACKSURFACE = addToSurface(BACKSURFACE, 0, 3, logoImage) where logoImage is a 2D list containing an ASCII image of my games logo, what I expect is for BACKSURFACE to now contain the logo at starting at BACKSURFACE[3][0] (coordinates 0, 3). Like I mentioned above, this works great. But for some reason, after creating my MIDSURFACE, and FRONTSURFACE lists, everytime I run addToSurface() all three lists are changed.
So now, when I run BACKSURFACE = (addToSurface(BACKSURFACE, 0, 3, logoImage)), not only does BACKSURFACE come with the logo rendered, so do MIDSURFACE and FRONTSURFACE, even though they haven't been referenced in the program at all after initialisation. For reference, here are my other functions in the program so far:
def __init__(height, width):
global SCREENWIDTH, SCREENHEIGHT, TITLE, BACKSURFACE
global MIDSURFACE, FRONTSURFACE, DISPLAYHEIGHT
SCREENWIDTH = width
SCREENHEIGHT = height
DISPLAYHEIGHT = height - 11 #The above 3 lines are just to set display size.
for i in range(DISPLAYHEIGHT): # This loop creates the 2D lists for all surfaces
temp = [] # where each pixel has a default value of [None]
BACKSURFACE.append(temp)
MIDSURFACE.append(temp)
FRONTSURFACE.append(temp)
for j in range(width):
temp.append([None])
def openImage(imageName, centre=False):
temp = []
dir = os.path.join(imageDirectory,imageName)
if not os.path.exists(dir):
return null
elif not centre:
with open(os.path.join(imageDirectory, imageName), "r") as f:
for line in f:
temp.append([char for char in line.rstrip("\n")])
f.close()
elif centre:
with open(os.path.join(imageDirectory, imageName), "r") as f:
for line in f:
centredLine = tech.centre(line.rstrip("\n"))
temp.append([char for char in centredLine])
f.close()
return temp
Like I mentioned above, I would describe myself as just past begginer level programming and this is definitely the most complex thing I've ever attempted. Even so, I have a feeling it's going to be something really simple. If you could point out where the error is and why it's an issue I'd be more than happy. If anyone could suggest a fix I'd appreciate it as well.
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.
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'm making a 2-d game using Pygame.
I want to add particle effects into the game I'm working on. I want to do things like spawn smoke, fire, blood, etc. I'm curious is there an easy way to do this? I don't really know even where to start.
I just need a base case I could expand upon..
Pls Help.
You might want to just make a class made of rects that go up and randomly go to the right or left each time it is updated for the smoke. Then make a ton of them whenever you want it. I'll try to make an example code below, but I can't guarntee it will work. You can make similar classes for the other particle effects.
class classsmoke(pygame.Rect):
'classsmoke(location)'
def __init__(self, location):
self.width=1
self.height=1
self.center=location
def update(self):
self.centery-=3#You might want to increase or decrease this
self.centerx+=random.randint(-2, 2)#You might want to raise or lower this as well
#use this to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put this somewhere within your game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, i)
Another option would be to make the class just a tuple, like this:
class classsmoke():
'classsmoke(location)'
def __init__(self, location):
self.center=location
def update(self):
self.center[1]-=3
self.center[0]+=random.randint(-2, 2)
#to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put inside game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i.center[0], i.center[1], 1, 1))
Or, to avoid classes completly:
#to create smoke:
smoke=[]
for i in range(20):
smoke.append(insert location here)
#put within your game loop
for i in smoke:
i[1]-=3
i[0]+=random.randint(-2, 2)
if i[1]<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i[0], i[1], 1, 1))
Pick your preference, and do something similar for other particle effects.
Check the Library for particle effects PyIgnition