Usually there are many characters in a game, and the "collision" between these characters is inevitable, such as whether the shell hit the plane or not. Collision detection is a very important problem in most games. In addition to the "collision" between characters, it is often used in game design to detect the collision between characters and specified colors, as well as the collision between characters and specific parts of another character. This paper introduces how to use pyGame mask. from_ The threshold () method creates a pyGame that implements the above functions mask. This method uses threshold instead of transparency to determine the pixels participating in the collision.
This method is equivalent to two methods. If only the first three parameters are used, this method returns a mask. Using this mask can detect the collision between another character and the specified color in the character with this mask. If parameter 4 is used, parameter 2 will be ignored, which is equivalent to another method, which will be discussed below.
If parameters 4 and 5 are not used, the official document gives the call format as follows, and the method returns a pyGame Mask, which generates collision response only for the color specified in parameter 2. Parameter 1 is the surface where the mask needs to be created, parameter 2 is the set color, and parameter 3 is the color threshold. Personal understanding is that there is a certain deviation between the actual color of the graphics in the surface and the color specified in parameter 2. This usage is successfully made into two examples.
pygame.mask.from_threshold(Surface,color,threshold=(0,0,0,255))-> Mask
In game programming, a character may be composed of different colors, but he wants to collide with only one color. The first example introduces the specific steps to realize this function. In this example, draw a large red ring with a blue solid circle inside, and then draw a small yellow circle. Move the small yellow circle close to the big circle. When you want to encounter the red circle of the big circle, you can't detect the collision. Only when you encounter the blue circle in the big red circle, you can detect the collision. When the collision occurs, change the background color of the window. The complete procedure is as follows.
import pygame class Circle(pygame.sprite.Sprite): def __init__(self,pos,color,radius,width): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface((100,100)) #Create a 100X100 Surface image instance self.image.set_colorkey((0,0,0)) #Set the color of the color (0,0,0) in the image to transparent self.image.fill((0, 0, 0)) #The background color is black. Due to the previous bar, the background color becomes transparent self.radius=radius self.width=width #Next sentence, draw a solid circle or circle on the image, self Width = 0, draw a solid circle, > 0, draw a ring pygame.draw.circle(self.image,pygame.Color(color),(50,50),self.radius,self.width) if self.width>0: #If self If width > 0, draw a solid blue circle after drawing a solid circle pygame.draw.circle(self.image,pygame.Color('blue'),(50,50),25,0) #Set the mask with the following formula. No matter how many colors the sprite has, the detection result is true only after blue collides with other sprites #For the resulting mask, only the position of blue is set to 1, and other colors and background colors are set to 0, that is, blue participates in collision detection self.mask=pygame.mask.from_threshold(self.image,pygame.Color('blue'),(1,1,1,255))#Torus character mask else: self.mask=pygame.mask.from_surface(self.image) #Yellow small round mask self.rect = self.image.get_rect(center=pos) #Move the image to the specified location def draw(self,aSurface): aSurface.blit(self.image,self.rect) #The class derived from Sprite is not put into the Group, and the class instance shows that the user-defined draw needs to be called pygame.init() screen = pygame.display.set_mode((200,100)) pygame.display.set_caption("Collision with blue") clock = pygame.time.Clock() circleRed=Circle((50,50),'red',45,20) #Create a large red Circle and an inner blue filled Circle, Circle instance circleyellow=Circle((150,50),'yellow',15,0) #Create a small yellow Circle, Circle instance run=True while run: screen.fill((255, 255, 255)) for event in pygame.event.get(): if event.type == pygame.QUIT: run=False if event.type==pygame.KEYDOWN: #Key press event if event.key==pygame.K_RIGHT: #Press the right arrow key on the keyboard to make the ball right circleyellow.rect.x+=5 elif event.key==pygame.K_LEFT: #Press the left arrow key on the keyboard to turn the ball to the left circleyellow.rect.x-=5 if pygame.sprite.collide_mask(circleRed,circleyellow): #Detect whether the two circles collide screen.fill((200, 200, 200)) #When a collision occurs, the form background turns gray else: screen.fill((255, 255, 255)) #No collision occurs, and the form background is white circleRed.draw(screen) circleyellow.draw(screen) clock.tick(10) pygame.display.update() pygame.quit()
In game programming, if different characters have different colors, you may want to collide when you encounter only one color character. For example, there are green apples and red apples. I just want to pick red apples. Another example is the aircraft war, the invasion of blue enemy aircraft, our side shoots enemy aircraft with anti-aircraft guns and sends red aircraft to fight, and the anti-aircraft guns should only hit enemy blue aircraft. The second example introduces the method of colliding only with characters with specified colors. In this example, there are 20 circular characters with different colors, whose positions are fixed, and one circle that can move with the mouse. The moving circle will collide only when it touches the circle with a fixed blue position. After the collision, the background color of the form will be changed. The complete procedure is as follows.
import pygame import random class Circle(pygame.sprite.Sprite): def __init__(self,pos,color,*grps): #*args indicates that there are multiple (indefinite) unknown parameters, which is essentially a tuple super().__init__(*grps) self.image = pygame.Surface((32, 32)) #Create a 32X32 Surface instance image self.image.set_colorkey((0,0,0)) #Set black to transparent in image self.image.fill((0, 0, 0)) #The background color is black. Due to the previous bar, the background color becomes transparent pygame.draw.circle(self.image, pygame.Color(color), (15, 15), 15) #Draw a circle on the image self.rect = self.image.get_rect(center=pos) #Move the image to the specified location screen = pygame.display.set_mode((800, 600)) colors = ['red', 'lightgreen', 'yellow', 'blue'] objects = pygame.sprite.Group() for _ in range(20): pos = random.randint(100, 700), random.randint(100, 600) Circle(pos, random.choice(colors), objects) #Create an instance of the Circle class and add it to the list objects for sprite in objects: #Set the mask for the Circle instance in the list. The mask cannot be set in the Circle class constructor. No error is reported and the result is incorrect sprite.mask=pygame.mask.from_threshold(sprite.image, pygame.Color('blue'),(1,1,1,255)) player = Circle(pygame.mouse.get_pos(), 'green') #The moving circle can collide with 20 circles with fixed positions run=True while run: for e in pygame.event.get(): if e.type == pygame.QUIT: run=False player.rect.center = pygame.mouse.get_pos() if pygame.sprite.spritecollideany(player, objects, pygame.sprite.collide_mask): screen.fill((200, 200, 200)) else: screen.fill((130, 130, 130)) objects.update() objects.draw(screen) screen.blit(player.image,player.rect) pygame.display.flip() pygame.quit()
About parameter 3: threshold, it means threshold in Chinese. For parameter 3, the official English document description is: the threshold range used to check the difference between two colors The threshold range used to detect the difference between two colors. Personal understanding may be that there is a certain deviation between the color value in the surface graph and the color value of parameter 2, that is, although parameter 2 is a solid color, some deviation of the color in parameter 1 graph can also cause collision, as long as it is less than the set threshold. In two successful examples, some experiments are done on the numerical range of parameter 3threshold. It is found that parameter 3 can be either rgba or rgb. It is also found that only considering rgb, the value range of R, g or b is a positive integer from 1 to 255, which cannot be 0, otherwise collision cannot occur, because the difference between two same colors is (0,0,0). In order to make (0,0,0) within the threshold, the threshold of each rgb color must be greater than or equal to 1. It can be inferred that the thresholds should be calculated separately according to the values of red, green and blue of the color and compared respectively. For example, if the parameter threshold=(1,2,3), the thresholds of red, green and blue are 1, 2 and 3 respectively. To compare whether the two colors are within the threshold, it is necessary to calculate the absolute value of the difference between red, green and blue, namely | r1-r2 |, | g1-g2 |, and | b1-b2 |. If the difference between red, green and blue is less than the threshold corresponding to threshold, it means that the difference between the two colors is within the threshold. In order to further verify the correctness of the above contents, continue to do some experiments on the above two programs. First list the color values used by the two programs: Red: 255,0,0, green: 0255,0, blue: 0,0255, white: 255255255, black: 0,0,0, light green: 144238153, yellow: 255255,0. In the first program, five colors of red, blue, black, white and yellow are used. Black is the bottom color of the picture of parameter 1(Surface class instance), white is the background color of the form, yellow is the color of the yellow circle, and white and yellow have nothing to do with the parameter threshold. Modify the value of parameter 3 in line 17 of example 1 to (255255255255). After the program runs, it can only collide with the blue circle. It is strange at first, but it is found that the program is correct after calculation. Calculate the absolute value of the difference between blue and black red blue: blue black = (0,0255), blue red = (255,0255), blue green = (0255255), blue blue blue = (0,0,0). Obviously, only blue is within the threshold and can produce collision. The second program uses all 7 colors listed above. After running, the green circle moves and collides only when it touches the blue circle at the fixed position, and cannot collide when it touches other fixed circles. Modify the parameter 3 = (255255255255) in line 19 of example 2. Run again, the green circle moves and collides with the blue circle and light green circle, but still cannot collide with the red and yellow circle. Calculate blue light green = (144238102), so the collision condition is met. Through the above experiments, it can be confirmed that the threshold format of parameter threshold is (Nr,Ng,Nb,a) or (Nr,Ng,Nb), and the value range of Nr, Ng and Nb is positive integer 1-255. The calculation method of the difference between two colors is to calculate the absolute value of the difference between red, green and blue of the two colors, i.e. | r1-r2 |, | g1-g2 |, and | b1-b2 |. Only when | r1-r2 | < Nr, | g1-g2 | < Ng and | b1-b2 | < NB are established at the same time, can the difference between the two colors be considered to be within the threshold. It can also be seen that the only basis for judging whether there is a collision is the threshold. As long as it is less than the threshold, all colors can collide. If you want to collide only with colors similar to blue, parameter 3 should be: (maximum allowable value of red, maximum allowable value of green and maximum allowable difference of blue). The larger the three values, the farther away from blue and the smaller the values, the closer to blue. The limit is (1,1,1). Most of these are personal opinions and may not be correct. I hope readers can correct them. I am very grateful.
If parameters 4 and 5 are used, the official document gives the call format as follows. Parameter 1 is the surface that needs to create a mask, parameter 2 is ignored and not used, parameter 3 is the color threshold, and parameter 4 is another surface. (the following is personal understanding) this method will subtract all pixel values (including the background color) of the surface represented by parameter 1 and all corresponding pixel values (including the background color) of the surface represented by parameter 4 one by one, and the difference is within the threshold range determined by parameter 3, Set it to 1 in the return mask, and the point will participate in collision detection.
pygame.mask.from_threshold(Surface,color,threshold=(0,0,0,255),othersurface=None,palette_colors=1)-> Mask
According to the above understanding of this method, the third program is written. Similar to the first procedure, this example draws a large red ring with a blue solid circle inside, and then draws a small yellow circle. Move the small yellow circle close to the big circle. When you want to touch the red circle of the big circle, you can't detect the collision. Only when you touch the blue circle in the big red circle can you detect the collision. The create mask statement is different from the first example. The create mask statement is as follows. Among them, parameter 1 is circleRed, which is the image with a blue solid circle Surface in the red ring, parameter 2 is ignored and not used, parameter 3 is the threshold, and parameter 4 is to create another Surface, circleRed1, which has the same width and height as circleRed, but only one blue solid circle, which has the same position and color as the solid blue circle of circleRed. Obviously, this statement cannot be placed at line 12, because circleRed1 does not exist at this time and should be placed at line 28.
circleRed.mask=pygame.mask.from_threshold(circleRed.image,pygame.Color('blue'),(1,1,1,255),circleRed1.image)
This method will circle red All pixel values of image (including background color) and circlered1 All the corresponding pixel values (including the background color) of the image are subtracted, and the pixels with the difference within the threshold (1,1,1255) return circlered Set to 1 in the mask. So what corresponding pixel colors are subtracted? First, circlered Image background and circlered1 Image background color, they should not participate in collision, so the two background colors should be different, circlered Image background is white (line 10), circlered1 The image background color is black (line 15). Then circle red Image red circle and circlered1 Image background color, obviously greater than the threshold. Finally, circlered Image blue and circlered1 Image blue, the same color, is obviously less than the threshold. With this setting, only the blue pixels corresponding to the positions in the two circles will be returned to circlered Set 1 in the mask to participate in collision detection.
Circle yellow. Is set The mask statement is placed at line 16 without error, but the correct mask cannot be obtained. Later, it is placed at line 29 to obtain the correct mask.
If you reduce the radius of the blue circle of circleRed1 or move the position of the blue circle, the collision position of parameter 1 will be affected. Use the statements in lines 30 and 31 to check whether the value of the specified location of the mask is 1 or 0. This is a method of program debugging.
The complete procedure is as follows. The effect drawing is the same as the first example.
import pygame class Circle(pygame.sprite.Sprite): def __init__(self,pos,color,radius,width): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface((100,100)) #Create a 100X100 Surface instance self.radius=radius self.width=width if self.width>0: self.image.set_colorkey((200,200,200)) #Set white to transparent in image self.image.fill((200,200,200)) #The background color is white. Due to the previous bar, the background color becomes transparent pygame.draw.circle(self.image,pygame.Color('blue'),(50,50),25,0) self.mask=None #Big circle mask else: self.image.set_colorkey((0,0,0)) #Set black to transparent in image self.image.fill((0, 0, 0)) #The background color is black. Due to the previous bar, the background color becomes transparent self.mask=None #pygame.mask.from_surface(self.image) #Failed to set mask here? pygame.draw.circle(self.image,pygame.Color(color),(50,50),self.radius,self.width) #Draw a circle on the image self.rect = self.image.get_rect(center=pos) #Move the image to the specified location def draw(self,aSurface): aSurface.blit(self.image,self.rect) #The class derived from Sprite is not put into the Group, and the class instance shows that the user-defined draw needs to be called pygame.init() screen = pygame.display.set_mode((300,100)) pygame.display.set_caption("Collision between rectangles") clock = pygame.time.Clock() circleRed=Circle((50,50),'red',45,20) #Create a blue Circle inside a large red Circle, Circle instance circleyellow=Circle((150,50),'yellow',15,0) #Create a small yellow Circle, Circle instance circleRed1=Circle((250,50),'blue',20,0) circleRed.mask=pygame.mask.from_threshold(circleRed.image,(0,0,255),(1,1,1,255),circleRed1.image) circleyellow.mask=pygame.mask.from_surface(circleyellow.image) #print(circleRed.mask.get_at((52,52))) #print(circleyellow.mask.get_at((52,52))) run=True while run: screen.fill((255, 255, 255)) for event in pygame.event.get(): if event.type == pygame.QUIT: run=False if event.type==pygame.KEYDOWN: #Key press event if event.key==pygame.K_RIGHT: #Press the right arrow key on the keyboard to make the ball right circleyellow.rect.x+=5 elif event.key==pygame.K_LEFT: #Press the left arrow key on the keyboard to turn the ball to the left circleyellow.rect.x-=5 if pygame.sprite.collide_mask(circleRed,circleyellow): screen.fill((200, 200, 200)) #When a collision occurs, the form background turns gray else: screen.fill((255, 255, 255)) #No collision occurs, and the form background is white circleRed1.draw(screen) circleRed.draw(screen) circleyellow.draw(screen) clock.tick(10) pygame.display.update() pygame.quit()