Learning Python's Pygame Development Contra (13)

Continue writing Contra

In the last blog learning Python: Pygame Development Contra (12) , we solved some problems. This time we added a new enemy, so let's start

The following is the material of the picture

Link: https://pan.baidu.com/s/1X7tESkes_O6nbPxfpHD6hQ?pwd=hdly
Extraction code: hdly

1. Create 2 types of enemies

Add a new enemy this time, first create the class of enemy 2

class Enemy2(pygame.sprite.Sprite):

    def __init__(self, x, y, direction, currentTime):
        pygame.sprite.Sprite.__init__(self)
        self.r = 0.0
        self.bulletPosition = 0
        self.rightImage = loadImage('../Image/Enemy/Enemy2/right.png')
        self.rightUpImage = loadImage('../Image/Enemy/Enemy2/rightUp.png')
        self.rightDownImage = loadImage('../Image/Enemy/Enemy2/rightDown.png')
        self.leftImage = loadImage('../Image/Enemy/Enemy2/right.png', True)
        self.leftUpImage = loadImage('../Image/Enemy/Enemy2/rightUp.png', True)
        self.leftDownImage = loadImage('../Image/Enemy/Enemy2/rightDown.png', True)
        self.type = 2
        if direction == Direction.RIGHT:
            self.image = self.rightImage
        else:
            self.image = self.leftImage

        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.center = self.rect.x + self.rect.width / 2, self.rect.y + self.rect.height / 2
        self.isDestroy = False
        self.isFiring = False
        self.life = 1
        self.lastTime = currentTime
        self.n = 0
        # 计算时间
        self.t = 0

Here are the loaded pictures and some necessary attributes

Here is a picture of the enemy

insert image description here
The rest assured that the enemy fires bullets is the center of the player, so we need to calculate the center of the player and the center of the enemy, so that we can calculate the distance between the player and the enemy in the x direction and the distance in the y direction

So we need a function to calculate the center of the enemy

def getCenter(self):
    return self.rect.x + self.rect.width / 2, self.rect.y + self.rect.height / 2

With the function of the calculation center, you can now write the draw() function

2. Write the draw() function of enemy 2

This enemy has a total of 6 states, the following is a schematic diagram
insert image description here

These six poses are six pictures

The muzzle of the enemy is always facing our center position, because in the Contra game, the bullets fired by the enemy follow the player all the time. When the player moves, it moves the position of the muzzle, so there are 6 postures

Next, we first calculate the distance between the enemy and the player

insert image description here

Through the figure, we can see that x and y are calculated using the center of the character.

∠1 is the angle between the line connecting the center of the player and the enemy and the horizontal direction, the size of this intersection determines the posture of the enemy

insert image description here
The blue line in this picture is the 45 degree line, so let's set the pose

When the player is on the left side of the enemy, calculate the angle between the player and the enemy. If it is greater than 45 degrees, the enemy is in posture 6. If it is less than -45 degrees, the enemy’s state is in posture 5. The postures of other enemies are calculated by this method.

Let's write the code below

def draw(self, window: pygame.Surface, player: PlayerOne, currentTime):
    # 获取玩家中心
    playerCenter = player.getCenter()
    # 获取敌人中心
    center = self.getCenter()
    # 计算距离
    y = playerCenter[1] - center[1]
    x = playerCenter[0] - center[0]
    # 设置存放夹角的变量
    r = 0
    # 当 x = 0时,此时玩家在敌人的正上方,我们不做任何操作
    if x != 0:
        # 如果玩家在敌人的正上方,计算角度
        r = math.atan(y / x) * 180 / math.pi
    # 设置变量,用来记录敌人的姿势,敌人的姿势就是发射子弹时的样子
    self.bulletPosition = 1
    # 根据距离的正负关系判断玩家在敌人的左边还是右边
    if x >= 0:
        if -45 < r < 45:
            self.bulletPosition = 2
            self.image = self.rightImage
        elif r >= 45:
            self.bulletPosition = 3
            self.image = self.rightDownImage
        elif r <= -45:
            self.bulletPosition = 1
            self.image = self.rightUpImage
    else:
        if -45 < r < 45:
            self.bulletPosition = 5
            self.image = self.leftImage
        elif r <= -45:
            self.bulletPosition = 4
            self.image = self.leftDownImage
        elif r >= 45:
            self.bulletPosition = 6
            self.image = self.leftUpImage
    self.r = r
    window.blit(self.image, self.rect)

I wrote a comment in the code. The meaning of this code is also explained above. It is probably to calculate the x-direction distance and y-direction distance between the player and the enemy, then calculate the angle, and set the enemy's posture according to the degree of the angle.

Now comes the crucial step, the position for firing bullets is available, the next thing to think about is: how to make the enemy fire at the center of the player?

Let's think about it. In high school, we learned distance = time × speed. Now we know the distance, which is the distance between the enemy and the center of the player. To calculate the speed, we need to know the time, so how to calculate the time? With time, we calculate the speed by the formula speed = distance / time

We can record the time interval of calling the draw() function, and set the average value of this as the time

So we have these two variables in the constructor

self.n = 0
# 时间
self.t = 0

n records the total number of intervals, t records the average value of the current interval

The following is the calculation idea: record the time interval of each call of the function, and calculate the average time interval of calling the function according to the number of calls, and this time interval is used as the firing speed of the bullet

self.n += 1
# 计算速度
total = self.t * self.n
total = total + abs(currentTime - self.lastTime)
self.lastTime = currentTime
self.t = total * 1.0 / (self.n + 1)

First, we record the time when the function was called last time in lastTime, and the time when the function is called this time is recorded in currentTime, and the currentTime value is passed in through the function parameter

total = self.t * self.n

This code to calculate the total time interval, because at the end we ask for the average of the interval between two calls to the function

Second, we use the following code to sum the total time interval

total = total + abs(currentTime - self.lastTime)

Then self.lastTime = currentTime to record the current time and prepare for the next calculation

Finally, calculate the mean of the interval

self.t = total * 1.0 / (self.n + 1)

As an example:

The time interval for calling this function is calculated for the first time is 7ms, at this time n = 1, t = 7, and the speed of the bullet is set to 7

The time interval for calling this function is calculated for the second time is 8ms. At this time, we need to calculate the average value of 8 and 7. At this time, n = 2, t = (1 * 7 + 8) / 2 = 7.5, so at this time Bullet speed is 7.5

The time interval for calling this function is calculated for the second time is 8ms. At this time, we still need to calculate the average value, so t = (7.5 * 2 + 8) / 3, and this result is still recorded as the speed of the bullet

By analogy, this interval will be updated every time, and this interval is the speed of the bullet

So, we finally calculated the velocity of the bullet

Write the code into the draw() function to complete the draw() function

def draw(self, window: pygame.Surface, player: PlayerOne, currentTime):

    self.n += 1
    # 计算时间
    total = self.t * self.n
    total = total + abs(currentTime - self.lastTime)
    self.lastTime = currentTime
    self.t = total * 1.0 / (self.n + 1)
    
    # 获取玩家中心
    playerCenter = player.getCenter()
    # 获取敌人中心
    center = self.getCenter()
    # 计算距离
    y = playerCenter[1] - center[1]
    x = playerCenter[0] - center[0]
    # 设置存放夹角的变量
    r = 0
    # 当 x = 0时,此时玩家在敌人的正上方,我们不做任何操作
    if x != 0:
        # 如果玩家在敌人的正上方,计算角度
        r = math.atan(y / x) * 180 / math.pi
    # 设置变量,用来记录敌人的姿势,敌人的姿势就是发射子弹时的样子
    self.bulletPosition = 1
    # 根据距离的正负关系判断玩家在敌人的左边还是右边
    if x >= 0:
        if -45 < r < 45:
            self.bulletPosition = 2
            self.image = self.rightImage
        elif r >= 45:
            self.bulletPosition = 3
            self.image = self.rightDownImage
        elif r <= -45:
            self.bulletPosition = 1
            self.image = self.rightUpImage
    else:
        if -45 < r < 45:
            self.bulletPosition = 5
            self.image = self.leftImage
        elif r <= -45:
            self.bulletPosition = 4
            self.image = self.leftDownImage
        elif r >= 45:
            self.bulletPosition = 6
            self.image = self.leftUpImage
    self.r = r
    window.blit(self.image, self.rect)

3. Write the function that the enemy crosses the boundary and disappears

When the enemy is created and we don’t destroy it, he will disappear in the player’s window as the player moves to the right. In order to avoid a lot of invalid enemy data in the program, we need to check the program and let those enemies who leave the window automatically destroy

write check function

def checkPosition(self, x, y):
    if abs(self.rect.x - x) > 2000:
        self.isDestroy = True
    elif abs(self.rect.y - y) > 600:
        self.isDestroy = True

Of course, enemy 1 also has this function

4. Write the enemy fire function

Since the bullet fired by enemy 2 is to be fired in the direction of the player, here we need to modify the bullet class

insert image description here
Change the initial velocity of the bullet to 0

Modify constructor parameters

insert image description here
enemyType is the type of enemy, the type is different, the launch position is different

parameter is some additional parameters. When enemy 2 fires bullets, some necessary information is passed in to calculate

Next, modify the original logic

insert image description here
Most of the code has not changed, an if-else statement has been added

Let's write the bullet logic code of enemy 2

elif enemyType == 2:
    self.index = 0
    bulletPosition = parameter[0]
    player = parameter[1]
    playerCenter = player.getCenter()
    if player.isDown or player.isSquating:
        # 下蹲、蹲下、在水中时,让人物中心下移动,下移动代表y坐标的值相加
        playerCenter = (playerCenter[0], playerCenter[1] + 8)
    elif player.isInWater:
        playerCenter = (playerCenter[0], playerCenter[1] + 15)
    t = parameter[2]
    # t *= 15
    r = parameter[3]
    if bulletPosition == 1:
        self.rect.x += 19 * PLAYER_SCALE
        self.rect.y += -1 * PLAYER_SCALE
        self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
    elif bulletPosition == 2:
        self.rect.x += 25 * PLAYER_SCALE
        self.rect.y += 10 * PLAYER_SCALE
        s = -1
        if r > 0:
            s = 1
        self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
    elif bulletPosition == 3:
        self.rect.x += 25 * PLAYER_SCALE
        self.rect.y += 25 * PLAYER_SCALE
        self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
    elif bulletPosition == 4:
        self.rect.x += -1 * PLAYER_SCALE
        self.rect.y += 25 * PLAYER_SCALE
        self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
    elif bulletPosition == 5:
        self.rect.x += -1 * PLAYER_SCALE
        self.rect.y += 10 * PLAYER_SCALE
        s = 1
        if r > 0:
            s = -1
        self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
    elif bulletPosition == 6:
        self.rect.x += -1 * PLAYER_SCALE
        self.rect.y += -1 * PLAYER_SCALE
        self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
        self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
    self.xSpeed /= 5
    self.ySpeed /= 5
self.image = self.images[self.index]

Complete bullet class constructor

def __init__(self, person, enemyType = 0, parameter = None):
    pygame.sprite.Sprite.__init__(self)
    self.images = [
        loadImage('../Image/Bullet/bullet1.png')
    ]
    self.index = 0
    # 速度
    self.xSpeed = 1
    self.ySpeed = 1
    self.rect = pygame.Rect(person.rect)

    # 类型0表示不是敌人
    if enemyType == 0:
        if person.isInWater:
            self.waterPosition(person)
        else:
            self.landPosition(person)

    # 敌人1
    elif enemyType == 1:

        self.index = 0
        if person.direction == Direction.RIGHT:
            self.rect.x += 27 * PLAYER_SCALE
            self.rect.y += 7 * PLAYER_SCALE
            self.ySpeed = 0
            self.xSpeed = 7
        else:
            self.rect.x += -1 * PLAYER_SCALE
            self.rect.y += 7 * PLAYER_SCALE
            self.ySpeed = 0
            self.xSpeed = -7
    # 敌人2
    elif enemyType == 2:
        self.index = 0
        # 从额外参数中获取敌人的姿势,即子弹的发射位置
        bulletPosition = parameter[0]
        # 获取玩家对象
        player = parameter[1]
        # 获取玩家中心
        playerCenter = player.getCenter()
        # 让人物中心下移
        if player.isDown or player.isSquating:
            # 下蹲、蹲下、在水中时,让人物中心下移动,下移动代表y坐标的值相加
            playerCenter = (playerCenter[0], playerCenter[1] + 8)
        elif player.isInWater:
            playerCenter = (playerCenter[0], playerCenter[1] + 15)
        # 获取子弹移动的时间
        t = parameter[2]
        # t *= 15
        # 获取敌人与玩家连线与水平方向的夹角
        r = parameter[3]
        # 根据子弹的发射位置(敌人的姿势)计算敌人的发射子弹的位置和子弹的速度
        if bulletPosition == 1:
            self.rect.x += 19 * PLAYER_SCALE
            self.rect.y += -1 * PLAYER_SCALE
            # 计算公式,|x0 - x1| / t = v
            self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
        elif bulletPosition == 2:
            self.rect.x += 25 * PLAYER_SCALE
            self.rect.y += 10 * PLAYER_SCALE
            # s 表示方向这里可以直接根据r的大小,计算出子弹的速度是减少还是增加
            # 减少表示向负方向移动
            s = -1
            if r > 0:
                s = 1
            self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
        elif bulletPosition == 3:
            self.rect.x += 25 * PLAYER_SCALE
            self.rect.y += 25 * PLAYER_SCALE
            self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
        elif bulletPosition == 4:
            self.rect.x += -1 * PLAYER_SCALE
            self.rect.y += 25 * PLAYER_SCALE
            self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
        elif bulletPosition == 5:
            self.rect.x += -1 * PLAYER_SCALE
            self.rect.y += 10 * PLAYER_SCALE
            s = 1
            if r > 0:
                s = -1
            self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
        elif bulletPosition == 6:
            self.rect.x += -1 * PLAYER_SCALE
            self.rect.y += -1 * PLAYER_SCALE
            self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
            self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
        self.xSpeed /= 5
        self.ySpeed /= 5
    self.image = self.images[self.index]

    # 销毁开关
    self.isDestroy = False

Then we will write the fire function of the player 2 class

def fire(self, enemyBulletList, player):
    i = random.randint(0, 30)
    if i == 5:
        self.isFiring = True
        enemyBulletList.append(Bullet(self, 2, (self.bulletPosition, player, self.t, self.r)))

Set the frequency of firing, create a bullet object when firing, and pass in the corresponding additional parameters

Ok, so far the enemy 2 category has been completed

5. Add enemy 2 to the map for testing

Come to the main class and write a global function to create enemy 2

insert image description here

def generateEnemy2(x, y):
    enemy = Enemy2(x, y, MainGame.player1, pygame.time.get_ticks())
    MainGame.enemyList.append(enemy)
    MainGame.allSprites.add(enemy)
    MainGame.enemyGroup.add(enemy)

Then call it in the generateEnemy() function

insert image description here
This code is to create an enemy 2 at the specified position

if -2005 < self.backRect.x < -2000:
    if self.enemyBoolList[2]:
        self.enemyBoolList[2] = False
        generateEnemy2(MainGame.player1.rect.x + 540, 465)

Next, run the game and see the effect

insert image description here
There is an error message, it should be that the constructor of the bullet class has been modified, and all functions that create bullets must be modified, and we will modify them one by one below

First enter the enemyUpdate() function. Since we have added enemy 2, we need to make a judgment when calling the draw() function, because the parameters of the draw() function of enemy 1 and enemy 2 are different.

insert image description here
modify the code

def enemyUpdate(enemyList, enemyBulletList):
    # 遍历整个敌人列表
    for enemy in enemyList:
        if enemy.type == 1:
            if enemy.isDestroy:
                enemyList.remove(enemy)
                MainGame.allSprites.remove(enemy)
                MainGame.enemyGroup.remove(enemy)
            else:
                enemy.checkPosition(MainGame.player1.rect.x, MainGame.player1.rect.y)
                enemy.draw(pygame.time.get_ticks())
                enemy.move(pygame.time.get_ticks())
                enemy.fire(enemyBulletList)
        elif enemy.type == 2:
            if enemy.isDestroy:
                enemyList.remove(enemy)
                MainGame.allSprites.remove(enemy)
                MainGame.enemyGroup.remove(enemy)
            else:
                enemy.checkPosition(MainGame.player1.rect.x, MainGame.player1.rect.y)
                enemy.draw(MainGame.window, MainGame.player1, pygame.time.get_ticks())
                enemy.fire(enemyBulletList, MainGame.player1)

The change is that the draw() function and fire() function of enemy 2 have one more parameter

Then add the member variable type to the enemy class 1

self.type = 1

insert image description here
Let's run the game again to see the problem

insert image description here
It can be seen that the direction of the enemy firing bullets is always towards the player

But there is still a problem, that is, when the player screen moves to the right, the bullet will stop

This should be when the window moves, and the bullets do not move together

Let's modify the mapObjectMove() function and add the following code

insert image description here
Theoretically, the explosion should also be added, we did not join, we also added the explosion

def mapObjectMove(self):
    for sprite in MainGame.allSprites:
        sprite.rect.x -= self.cameraAdaption
    for collider in MainGame.playerColliderGroup:
        collider.rect.x -= self.cameraAdaption
    for collider in MainGame.colliderStack:
        collider.rect.x -= self.cameraAdaption
    for collider in MainGame.enemyColliderGroup:
        collider.rect.x -= self.cameraAdaption
    for bullet in MainGame.enemyBulletList:
        bullet.rect.x -= self.cameraAdaption
    for explode in MainGame.explodeList:
        explode.rect.x -= self.cameraAdaption

insert image description here
Ok, let's run the game again to see the effect

insert image description here
As you can see, the bullet will also move as the window moves

We are done with Enemy 2 now, the next step is to add other enemies and BOSS

complete main class code

import copy
import sys
import pygame
from Constants import *
from PlayerOne import PlayerOne
from Collider import Collider
from Enemy1 import Enemy1
from Explode import Explode
from Enemy2 import Enemy2


def drawPlayerOneBullet(player1BulletList):
    for bullet in player1BulletList:
        if bullet.isDestroy:
            player1BulletList.remove(bullet)
        else:
            bullet.draw(MainGame.window)
            bullet.move()
            bullet.collideEnemy(MainGame.enemyList, MainGame.explodeList)

def enemyUpdate(enemyList, enemyBulletList):
    # 遍历整个敌人列表
    for enemy in enemyList:
        if enemy.type == 1:
            if enemy.isDestroy:
                enemyList.remove(enemy)
                MainGame.allSprites.remove(enemy)
                MainGame.enemyGroup.remove(enemy)
            else:
                enemy.checkPosition(MainGame.player1.rect.x, MainGame.player1.rect.y)
                enemy.draw(pygame.time.get_ticks())
                enemy.move(pygame.time.get_ticks())
                enemy.fire(enemyBulletList)
        elif enemy.type == 2:
            if enemy.isDestroy:
                enemyList.remove(enemy)
                MainGame.allSprites.remove(enemy)
                MainGame.enemyGroup.remove(enemy)
            else:
                enemy.checkPosition(MainGame.player1.rect.x, MainGame.player1.rect.y)
                enemy.draw(MainGame.window, MainGame.player1, pygame.time.get_ticks())
                enemy.fire(enemyBulletList, MainGame.player1)


def updateEnemyPosition():
    # 遍历全部敌人列表
    for enemy in MainGame.enemyList:
        # 创建一个复制
        t = copy.copy(enemy)
        t.rect.y += 1
        # 让复制的y加1,看看有没有发生碰撞,这里看的碰撞是enemyColliderGroup中的碰撞
        collide = pygame.sprite.spritecollideany(t, MainGame.enemyColliderGroup)
        # 没有发生碰撞,让敌人下落
        if not collide:
            enemy.rect.y += 4
            enemy.isFalling = True
            # 改变下落时的图片
            enemy.image = enemy.rightFallImage if enemy.direction == Direction.RIGHT else enemy.leftFallImage
        else:
            enemy.isFalling = False
            # 如果与河发生碰撞,表示敌人落到了水中,那么敌人直接死亡
            if collide in MainGame.enemyRiverGroup:
                enemy.isDestroy = True
                MainGame.explodeList.append(Explode(enemy))
        t.rect.y -= 1

def drawEnemyBullet(enemyBulletList):
    for bullet in enemyBulletList:
        if bullet.isDestroy:
            enemyBulletList.remove(bullet)
        else:
            bullet.draw(MainGame.window)
            bullet.move()
            if bullet.collidePlayer(MainGame.player1, MainGame.explodeList):
                initPlayer1(MainGame.player1.life)


def initLand():
    land1 = Collider(81, 119 * MAP_SCALE, 737 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    # land1 = Collider(81, 119 * MAP_SCALE, 8000 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land2 = Collider(400, 151 * MAP_SCALE, 96 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land3 = Collider(640, 183 * MAP_SCALE, 33 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land4 = Collider(880, 183 * MAP_SCALE, 33 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land5 = Collider(720, 215 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land6 = Collider(1040, 154 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land7 = Collider(1600, 166 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land8 = Collider(1120 * RATIO, 215 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land9 = Collider(1650 * RATIO, 119 * MAP_SCALE, 5 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land10 = Collider(2185 * RATIO, 119 * MAP_SCALE, 8 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land11 = Collider(2595 * RATIO, 215 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land12 = Collider(2770 * RATIO, 167 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land13 = Collider(2535 * RATIO, 87 * MAP_SCALE, 16 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land14 = Collider(2950 * RATIO, 151 * MAP_SCALE, 7 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land15 = Collider(3185 * RATIO, 215 * MAP_SCALE, 6 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land16 = Collider(3420 * RATIO, 119 * MAP_SCALE, 7 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land17 = Collider(3537 * RATIO, 183 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land18 = Collider(3715 * RATIO, 183 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land19 = Collider(3890 * RATIO, 167 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land20 = Collider(3775 * RATIO, 87 * MAP_SCALE, 5 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land21 = Collider(4010 * RATIO, 151 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land22 = Collider(4125 * RATIO, 119 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land23 = Collider(4304 * RATIO, 151 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land24 = Collider(4304 * RATIO, 216 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land25 = Collider(4361 * RATIO, 183 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land26 = Collider(4537 * RATIO, 119 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land27 = Collider(4598 * RATIO, 87 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land28 = Collider(4657 * RATIO, 167 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land29 = Collider(4598 * RATIO, 216 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land30 = Collider(4776 * RATIO, 119 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land31 = Collider(4835 * RATIO, 151 * MAP_SCALE, 5 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land32 = Collider(5010 * RATIO, 216 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land33 = Collider(5250 * RATIO, 183 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land34 = Collider(5423 * RATIO, 151 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land35 = Collider(5543 * RATIO, 119 * MAP_SCALE, 4 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land36 = Collider(5601 * RATIO, 167 * MAP_SCALE, 3 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land37 = Collider(5541 * RATIO, 216 * MAP_SCALE, 8 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land38 = Collider(5776 * RATIO, 151 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    land39 = Collider(5836 * RATIO, 183 * MAP_SCALE, 1 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    MainGame.playerLandGroup = pygame.sprite.Group(
        land1, land2, land3, land4, land5, land6, land7, land8, land9, land10,
        land11, land12, land13, land14, land15, land16, land17, land18, land19, land20,
        land21, land22, land23, land24, land25, land26, land27, land28, land29, land30,
        land31, land32, land33, land34, land35, land36, land37, land38, land39
    )
    eland1 = Collider(81, 119 * MAP_SCALE, 737 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    eland8 = Collider(1120 * RATIO, 215 * MAP_SCALE, 2 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    eland9 = Collider(1650 * RATIO, 119 * MAP_SCALE, 5 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    eland10 = Collider(2185 * RATIO, 119 * MAP_SCALE, 8 * LAND_LENGTH * MAP_SCALE, LAND_THICKNESS * MAP_SCALE)
    MainGame.enemyLandGroup = pygame.sprite.Group(eland1, eland8, eland9, eland10)
    MainGame.playerColliderGroup.add(MainGame.playerLandGroup)
    MainGame.enemyColliderGroup.add(MainGame.enemyLandGroup)




def initRiver():
    river1 = Collider(0, 215 * MAP_SCALE, 289 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE, (0, 0, 255))
    river2 = Collider(880, 215 * MAP_SCALE, 255 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE, (0, 0, 255))
    river3 = Collider(1680, 215 * MAP_SCALE, 737 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE, (0, 0, 255))
    eRiver1 = Collider(0, 215 * MAP_SCALE, 289 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE, (0, 0, 255))
    eRiver3 = Collider(1680, 215 * MAP_SCALE, 737 * MAP_SCALE, LAND_THICKNESS * MAP_SCALE, (0, 0, 255))
    MainGame.playerRiverGroup = pygame.sprite.Group(river1, river2, river3)
    MainGame.enemyRiverGroup = pygame.sprite.Group(eRiver1, eRiver3)
    MainGame.playerColliderGroup.add(MainGame.playerRiverGroup)
    MainGame.enemyColliderGroup.add(MainGame.enemyRiverGroup)

def drawExplode(explodeList):
    for explode in explodeList:
        if explode.isDestroy:
            explodeList.remove(explode)
        else:
            if explode.isUseTime:
                explode.draw(MainGame.window, pygame.time.get_ticks())
            else:
                explode.draw(MainGame.window)

def initPlayer1(life):
    if life == 0:
        pass
    MainGame.allSprites.remove(MainGame.player1)
    MainGame.player1 = PlayerOne(pygame.time.get_ticks(), life)
    MainGame.player1.rect.x = 80
    MainGame.player1.rect.bottom = 0
    # 把角色放入组中,方便统一管理
    MainGame.allSprites.add(MainGame.player1)


def generateEnemy1(x, y, direction, currentTime):
    # 根据玩家的当前位置和方向产生一个敌人
    enemy = Enemy1(x, y, direction, currentTime)
    # 分别加入敌人列表,所有角色组,敌人碰撞组
    MainGame.enemyList.append(enemy)
    MainGame.allSprites.add(enemy)
    MainGame.enemyGroup.add(enemy)

def generateEnemy2(x, y):
    enemy = Enemy2(x, y, MainGame.player1, pygame.time.get_ticks())
    MainGame.enemyList.append(enemy)
    MainGame.allSprites.add(enemy)
    MainGame.enemyGroup.add(enemy)


class MainGame:

    player1 = None
    allSprites = pygame.sprite.Group()

    # 敌人
    enemyList = []

    window = None
    # 子弹
    player1BulletList = []
    enemyBulletList = []

    # 爆炸效果
    explodeList = []

    # 冲突
    playerLandGroup = pygame.sprite.Group()
    playerRiverGroup = pygame.sprite.Group()
    enemyLandGroup = pygame.sprite.Group()
    enemyRiverGroup = pygame.sprite.Group()
    playerColliderGroup = pygame.sprite.Group()
    enemyColliderGroup = pygame.sprite.Group()
    enemyGroup = pygame.sprite.Group()
    bridgeGroup = pygame.sprite.Group()

    # 冲突栈
    colliderStack = []

    def __init__(self):

        # 设置成员变量
        self.background = None
        self.backRect = None
        self.enemyBoolList = [True for _ in range(5)]

        # 初始化展示模块
        pygame.display.init()

        SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT)
        # 初始化窗口
        MainGame.window = pygame.display.set_mode(SCREEN_SIZE)
        # 设置窗口标题
        pygame.display.set_caption('魂斗罗角色')
        # 是否结束游戏
        self.isEnd = False
        # 获取按键
        self.keys = pygame.key.get_pressed()
        # 帧率
        self.fps = 60
        self.clock = pygame.time.Clock()

        # 角色
        initPlayer1(3)

        # 加载背景
        self.initBackground()

        # 摄像头调整
        self.cameraAdaption = 0

        # 加载场景景物
        initLand()
        initRiver()

        # 碰撞失效间隔
        self.index = 0

        # 显示玩家生命值
        self.lifeImage = loadImage('../Image/Player/Player1/Life/life.png')

    def run(self):
        while not self.isEnd:

            # 设置背景颜色
            pygame.display.get_surface().fill((0, 0, 0))

            # 游戏场景和景物更新函数
            self.update(MainGame.window, MainGame.player1BulletList)

            # 获取窗口中的事件
            self.getPlayingModeEvent()

            # 更新窗口
            pygame.display.update()

            # 设置帧率
            self.clock.tick(self.fps)
            fps = self.clock.get_fps()
            caption = '魂斗罗 - {:.2f}'.format(fps)
            pygame.display.set_caption(caption)
        else:
            sys.exit()

    def getPlayingModeEvent(self):
        # 获取事件列表
        for event in pygame.event.get():
            # 点击窗口关闭按钮
            if event.type == pygame.QUIT:
                self.isEnd = True
            # 键盘按键按下
            elif event.type == pygame.KEYDOWN:
                self.keys = pygame.key.get_pressed()
            # 键盘按键抬起
            elif event.type == pygame.KEYUP:
                self.keys = pygame.key.get_pressed()

    def update(self, window, player1BulletList):
        # 加载背景
        window.blit(self.background, self.backRect)

        # 显示生命图标
        self.drawLifeImage(MainGame.window)

        # 敌人更新
        enemyUpdate(MainGame.enemyList, MainGame.enemyBulletList)
        drawExplode(MainGame.explodeList)
        drawPlayerOneBullet(MainGame.player1BulletList)
        drawEnemyBullet(MainGame.enemyBulletList)
        # 更新人物
        currentTime = pygame.time.get_ticks()
        MainGame.allSprites.update(self.keys, currentTime, player1BulletList)
        self.updatePlayerPosition()
        updateEnemyPosition()
        # 摄像机移动
        self.camera()
        # 显示物体
        MainGame.allSprites.draw(window)
        # 加载敌人
        self.generateEnemy()

        for collider in MainGame.playerLandGroup:
            r = collider.draw(window, self.player1.rect.y)
            # 如果没有画出来,表示玩家高度低于直线,所有把直线从组中删除
            if not r:
                # 删除前先检查一下是不是在组中
                if collider in MainGame.playerColliderGroup:
                    # 删除并加入栈
                    MainGame.colliderStack.insert(0, collider)
                    MainGame.playerColliderGroup.remove(collider)
            else:
                # 如果画出来了,判断一下玩家距离是否高于线的距离
                if collider.rect.y > self.player1.rect.bottom:
                    # 如果是的话,且冲突栈不为空,那么从栈中取出一个元素放入冲突组,最前面的元素一定是先如队列的
                    if len(MainGame.colliderStack) > 0:
                        f = MainGame.colliderStack.pop()
                        MainGame.playerColliderGroup.add(f)
        MainGame.playerRiverGroup.draw(window)

    def camera(self):
        # 如果玩家的右边到达了屏幕的一半
        if self.player1.rect.right > SCREEN_WIDTH / 2:
            if not (self.backRect.x <= -3500 * MAP_SCALE):
                # 计算出超过的距离
                self.cameraAdaption = self.player1.rect.right - SCREEN_WIDTH / 2
                # 让背景向右走这么多距离
                self.backRect.x -= self.cameraAdaption
                # 场景中的物体都走这么多距离
                self.mapObjectMove()

    def mapObjectMove(self):
        for sprite in MainGame.allSprites:
            sprite.rect.x -= self.cameraAdaption
        for collider in MainGame.playerColliderGroup:
            collider.rect.x -= self.cameraAdaption
        for collider in MainGame.colliderStack:
            collider.rect.x -= self.cameraAdaption
        for collider in MainGame.enemyColliderGroup:
            collider.rect.x -= self.cameraAdaption
        for bullet in MainGame.enemyBulletList:
            bullet.rect.x -= self.cameraAdaption
        for explode in MainGame.explodeList:
            explode.rect.x -= self.cameraAdaption

    def updatePlayerPosition(self):
        # 在index的循环次数中,不进行碰撞检测,用来让玩家向下跳跃
        if self.index > 0:
            self.index -= 1
            self.player1.rect.x += self.player1.xSpeed
            self.player1.rect.y += self.player1.ySpeed
            self.player1.isDown = False
        else:
            # 首先更新y的位置
            self.player1.rect.y += self.player1.ySpeed
            # 玩家向下跳跃,35次循环内不进行碰撞检测
            if self.player1.state == State.JUMP and self.player1.isDown:
                self.index = 35
            # 玩家向上跳跃,15次循环内不进行碰撞检测
            elif self.player1.state == State.JUMP and self.player1.isUp:
                self.index = 15
            else:
                # 检测碰撞
                # 这里是玩家和所有碰撞组中的碰撞体检测碰撞,如果发生了碰撞,就会返回碰撞到的碰撞体对象
                collider = pygame.sprite.spritecollideany(self.player1, MainGame.playerColliderGroup)
                # 如果发生碰撞,判断是不是在河里
                if collider in MainGame.playerRiverGroup:
                    self.riverCollide()
                # 判断是不是在陆地上
                elif collider in MainGame.playerLandGroup:
                        self.player1.isInWater = False
                # 如果发生碰撞
                if collider:
                    if MainGame.player1.isInvincible:
                        # 玩家落地不无敌
                        MainGame.player1.isInvincible = False
                    # 判断一下人物的y速度,如果大于0,则说明玩家已经接触到了碰撞体表面,需要让玩家站在表面,不掉下去
                    if self.player1.ySpeed > 0:
                        self.player1.ySpeed = 0
                        self.player1.state = State.WALK
                        self.player1.rect.bottom = collider.rect.top
                else:
                    # 否则的话,我们创建一个玩家的复制
                    tempPlayer = copy.copy(self.player1)
                    # 让玩家的纵坐标—+1,看看有没有发生碰撞
                    tempPlayer.rect.y += 1
                    # 如果没有发生碰撞,就说明玩家下面不是碰撞体,是空的
                    if not pygame.sprite.spritecollideany(tempPlayer, MainGame.playerColliderGroup):
                        # 如果此时不是跳跃状态,那么就让玩家变成下落状态,因为玩家在跳跃时,是向上跳跃,不需要对下面的物体进行碰撞检测
                        if tempPlayer.state != State.JUMP:
                            self.player1.state = State.FALL
                    tempPlayer.rect.y -= 1

                # 与敌人碰撞
                if pygame.sprite.spritecollideany(MainGame.player1, MainGame.enemyGroup):
                    if MainGame.player1.damage(1):
                        MainGame.explodeList.append(Explode(MainGame.player1, ExplodeVariety.PLAYER1))
                        initPlayer1(MainGame.player1.life)

            # 更新x的位置
            self.player1.rect.x += self.player1.xSpeed
            # 同样的检查碰撞
            collider = pygame.sprite.spritecollideany(self.player1, MainGame.playerColliderGroup)
            # 如果发生了碰撞
            if collider:
                # 判断玩家的x方向速度,如果大于0,表示右边有碰撞体
                if self.player1.xSpeed > 0:
                    # 设置玩家的右边等于碰撞体的左边
                    self.player1.rect.right = collider.rect.left
                else:
                    # 左边有碰撞体
                    self.player1.rect.left = collider.rect.right
                self.player1.xSpeed = 0

            tempPlayer = copy.copy(self.player1)
            tempPlayer.rect.y += 1
            if c := pygame.sprite.spritecollideany(tempPlayer, MainGame.playerColliderGroup):
                if c in MainGame.playerLandGroup:
                    self.player1.isInWater = False
                elif c in MainGame.playerRiverGroup:
                    self.player1.isInWater = True
            tempPlayer.rect.y -= 1

    def riverCollide(self):
        # 在河里设置isInWater
        self.player1.isInWater = True
        # 设置玩家在河里不能跳跃
        self.player1.isJumping = False
        # 默认落下去是站在河里的
        self.player1.isStanding = True
        # 玩家方向不能向下
        self.player1.isDown = False
        # 根据玩家方向,加载落入河中的一瞬间的图片
        if self.player1.direction == Direction.RIGHT:
            self.player1.image = self.player1.rightInWaterImage
        else:
            self.player1.image = self.player1.leftInWaterImage

    def generateEnemy(self):
        if -1505 < self.backRect.x < -1500:
            if self.enemyBoolList[0]:
                self.enemyBoolList[0] = False
                generateEnemy1(MainGame.player1.rect.x + 600, POSITION_1, Direction.LEFT, pygame.time.get_ticks())
                generateEnemy1(MainGame.player1.rect.x - 360, POSITION_1, Direction.RIGHT, pygame.time.get_ticks())

        if -1705 < self.backRect.x < -1700:
            if self.enemyBoolList[1]:
                self.enemyBoolList[1] = False
                generateEnemy1(MainGame.player1.rect.x - 360, POSITION_1, Direction.RIGHT, pygame.time.get_ticks())
                generateEnemy1(MainGame.player1.rect.x - 400, POSITION_1, Direction.RIGHT,
                               pygame.time.get_ticks())

        if -2005 < self.backRect.x < -2000:
            if self.enemyBoolList[2]:
                self.enemyBoolList[2] = False
                generateEnemy2(MainGame.player1.rect.x + 540, 465)


    def initBackground(self):
        # 读取背景图片
        self.background = pygame.image.load('../Image/Map/1/Background/First(No Bridge).png')
        self.backRect = self.background.get_rect()
        self.background = pygame.transform.scale(
            self.background,
            (int(self.backRect.width * MAP_SCALE),
             int(self.backRect.height * MAP_SCALE))
        )
        self.backRect.x = -1280

    def drawLifeImage(self, window):
        # 如果玩家的生命值大于3,那么生命值图标就显示3个
        if MainGame.player1.life > 3:
            number = 3
        # 否则,有几个显示几个,肯定不超过三个
        else:
            number = MainGame.player1.life
        rect = self.lifeImage.get_rect()
        # 设置生命值图标的显示位置
        rect.y = 5
        for i in range(number):
            # 每个图标之间的距离为25像素
            rect.x = 5 + i * 20
            window.blit(self.lifeImage, rect)

if __name__ == '__main__':
    MainGame().run()

Complete Enemy Class 1 Code

import random

import pygame
from Constants import *
from Bullet import Bullet

class Enemy1(pygame.sprite.Sprite):

    def __init__(self, x, y, direction, currentTime):
        pygame.sprite.Sprite.__init__(self)

        self.lastTime = currentTime
        self.fireTime = currentTime
        self.rightImages = [
            loadImage('../Image/Enemy/Enemy1/1.png'),
            loadImage('../Image/Enemy/Enemy1/2.png'),
            loadImage('../Image/Enemy/Enemy1/3.png')
        ]
        self.leftImages = [
            loadImage('../Image/Enemy/Enemy1/1.png', True),
            loadImage('../Image/Enemy/Enemy1/2.png', True),
            loadImage('../Image/Enemy/Enemy1/3.png', True)
        ]
        self.rightFireImage = loadImage('../Image/Enemy/Enemy1/fire.png')
        self.leftFireImage = loadImage('../Image/Enemy/Enemy1/fire.png', True)
        self.fallImage = loadImage('../Image/Enemy/Enemy1/fall.png', True)

        self.rightFireImage = loadImage('../Image/Enemy/Enemy1/fire.png')
        self.leftFireImage = loadImage('../Image/Enemy/Enemy1/fire.png', True)
        self.rightFallImage = loadImage('../Image/Enemy/Enemy1/fall.png')
        self.leftFallImage = loadImage('../Image/Enemy/Enemy1/fall.png', True)

        self.index = 0
        self.direction = direction
        if self.direction == Direction.RIGHT:
            self.image = self.rightImages[self.index]
        else:
            self.image = self.leftImages[self.index]
        self.rect = self.image.get_rect()
        self.isFalling = False
        self.rect.x = x
        self.rect.y = y
        self.speed = 3
        self.isDestroy = False
        self.isFiring = False
        self.life = 1
        self.type = 1

    def move(self, currentTime):
        # 首先判断敌人是否开火,如果是开火状态,就不能移动
        if not self.isFiring:
            # 没有开火,就根据方向移动,这里我设置敌人只能向一个方向移动,不能转身
            if self.direction == Direction.RIGHT:
                self.rect.left += self.speed
            else:
                self.rect.left -= self.speed
        else:
            # 如果此时是开火状态,判断一下上次开火的时间和这次的时间是否相差1000
            # 这个的作用在于让敌人开火的时候站在那里不动,因为敌人移动时是不能开火的
            if currentTime - self.fireTime > 1000:
                # 如果两次开火间隔相差很大,那么就可以让敌人再次开火
                self.isFiring = False
                self.fireTime = currentTime

    def draw(self, currentTime):
        if self.isFiring:
            if self.direction == Direction.RIGHT:
                self.image = self.rightFireImage
            else:
                self.image = self.leftFireImage
        else:
            if currentTime - self.lastTime > 115:
                if self.index < 2:
                    self.index += 1
                else:
                    self.index = 0
                self.lastTime = currentTime
            if self.direction == Direction.RIGHT:
                self.image = self.rightImages[self.index]
            else:
                self.image = self.leftImages[self.index]

    def fire(self, enemyBulletList):
        if not self.isFalling:
            i = random.randint(0, 50)
            if i == 5:
                if not self.isFiring:
                    self.isFiring = True
                    enemyBulletList.append(Bullet(self, True))

    def checkPosition(self, x, y):
        if abs(self.rect.x - x) > 1000:
            self.isDestroy = True
        elif abs(self.rect.y - y) > 600:
            self.isDestroy = True

Complete Enemy 2 Class Codes

import math
import random

import pygame
from Constants import *
from Bullet import Bullet
from PlayerOne import PlayerOne


class Enemy2(pygame.sprite.Sprite):

    def __init__(self, x, y, direction, currentTime):
        pygame.sprite.Sprite.__init__(self)
        self.r = 0.0
        self.bulletPosition = 0
        self.rightImage = loadImage('../Image/Enemy/Enemy2/right.png')
        self.rightUpImage = loadImage('../Image/Enemy/Enemy2/rightUp.png')
        self.rightDownImage = loadImage('../Image/Enemy/Enemy2/rightDown.png')
        self.leftImage = loadImage('../Image/Enemy/Enemy2/right.png', True)
        self.leftUpImage = loadImage('../Image/Enemy/Enemy2/rightUp.png', True)
        self.leftDownImage = loadImage('../Image/Enemy/Enemy2/rightDown.png', True)
        self.type = 2
        if direction == Direction.RIGHT:
            self.image = self.rightImage
        else:
            self.image = self.leftImage

        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.center = self.rect.x + self.rect.width / 2, self.rect.y + self.rect.height / 2
        self.isDestroy = False
        self.isFiring = False
        self.life = 1
        self.lastTime = currentTime
        self.n = 0
        # 计算时间
        self.t = 0

    def getCenter(self):
        return self.rect.x + self.rect.width / 2, self.rect.y + self.rect.height / 2

    def draw(self, window: pygame.Surface, player: PlayerOne, currentTime):

        self.n += 1
        # 计算时间
        total = self.t * self.n
        total = total + abs(currentTime - self.lastTime)
        self.lastTime = currentTime
        self.t = total * 1.0 / (self.n + 1)

        # 获取玩家中心
        playerCenter = player.getCenter()
        # 获取敌人中心
        center = self.getCenter()
        # 计算距离
        y = playerCenter[1] - center[1]
        x = playerCenter[0] - center[0]
        # 设置存放夹角的变量
        r = 0
        # 当 x = 0时,此时玩家在敌人的正上方,我们不做任何操作
        if x != 0:
            # 如果玩家在敌人的正上方,计算角度
            r = math.atan(y / x) * 180 / math.pi
        # 设置变量,用来记录敌人的姿势,敌人的姿势就是发射子弹时的样子
        self.bulletPosition = 1
        # 根据距离的正负关系判断玩家在敌人的左边还是右边
        if x >= 0:
            if -45 < r < 45:
                self.bulletPosition = 2
                self.image = self.rightImage
            elif r >= 45:
                self.bulletPosition = 3
                self.image = self.rightDownImage
            elif r <= -45:
                self.bulletPosition = 1
                self.image = self.rightUpImage
        else:
            if -45 < r < 45:
                self.bulletPosition = 5
                self.image = self.leftImage
            elif r <= -45:
                self.bulletPosition = 4
                self.image = self.leftDownImage
            elif r >= 45:
                self.bulletPosition = 6
                self.image = self.leftUpImage
        self.r = r
        window.blit(self.image, self.rect)

    def fire(self, enemyBulletList, player):
        i = random.randint(0, 30)
        if i == 5:
            self.isFiring = True
            enemyBulletList.append(Bullet(self, 2, (self.bulletPosition, player, self.t, self.r)))

    def checkPosition(self, x, y):
        if abs(self.rect.x - x) > 2000:
            self.isDestroy = True
        elif abs(self.rect.y - y) > 600:
            self.isDestroy = True



Complete bullet class code

import pygame
from Constants import *
from Explode import Explode


class Bullet(pygame.sprite.Sprite):

    def __init__(self, person, enemyType = 0, parameter = None):
        pygame.sprite.Sprite.__init__(self)
        self.images = [
            loadImage('../Image/Bullet/bullet1.png')
        ]
        self.index = 0
        # 速度
        self.xSpeed = 1
        self.ySpeed = 1
        self.rect = pygame.Rect(person.rect)

        # 类型0表示不是敌人
        if enemyType == 0:
            if person.isInWater:
                self.waterPosition(person)
            else:
                self.landPosition(person)

        # 敌人1
        elif enemyType == 1:

            self.index = 0
            if person.direction == Direction.RIGHT:
                self.rect.x += 27 * PLAYER_SCALE
                self.rect.y += 7 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = 7
            else:
                self.rect.x += -1 * PLAYER_SCALE
                self.rect.y += 7 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = -7
        # 敌人2
        elif enemyType == 2:
            self.index = 0
            # 从额外参数中获取敌人的姿势,即子弹的发射位置
            bulletPosition = parameter[0]
            # 获取玩家对象
            player = parameter[1]
            # 获取玩家中心
            playerCenter = player.getCenter()
            # 让人物中心下移
            if player.isDown or player.isSquating:
                # 下蹲、蹲下、在水中时,让人物中心下移动,下移动代表y坐标的值相加
                playerCenter = (playerCenter[0], playerCenter[1] + 8)
            elif player.isInWater:
                playerCenter = (playerCenter[0], playerCenter[1] + 15)
            # 获取子弹移动的时间
            t = parameter[2]
            # t *= 15
            # 获取敌人与玩家连线与水平方向的夹角
            r = parameter[3]
            # 根据子弹的发射位置(敌人的姿势)计算敌人的发射子弹的位置和子弹的速度
            if bulletPosition == 1:
                self.rect.x += 19 * PLAYER_SCALE
                self.rect.y += -1 * PLAYER_SCALE
                # 计算公式,|x0 - x1| / t = v
                self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
            elif bulletPosition == 2:
                self.rect.x += 25 * PLAYER_SCALE
                self.rect.y += 10 * PLAYER_SCALE
                # s 表示方向这里可以直接根据r的大小,计算出子弹的速度是减少还是增加
                # 减少表示向负方向移动
                s = -1
                if r > 0:
                    s = 1
                self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
            elif bulletPosition == 3:
                self.rect.x += 25 * PLAYER_SCALE
                self.rect.y += 25 * PLAYER_SCALE
                self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = abs(self.rect.x - playerCenter[0]) * 1.0 / t
            elif bulletPosition == 4:
                self.rect.x += -1 * PLAYER_SCALE
                self.rect.y += 25 * PLAYER_SCALE
                self.ySpeed = abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
            elif bulletPosition == 5:
                self.rect.x += -1 * PLAYER_SCALE
                self.rect.y += 10 * PLAYER_SCALE
                s = 1
                if r > 0:
                    s = -1
                self.ySpeed = s * abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
            elif bulletPosition == 6:
                self.rect.x += -1 * PLAYER_SCALE
                self.rect.y += -1 * PLAYER_SCALE
                self.ySpeed = - abs(self.rect.y - playerCenter[1]) * 1.0 / t
                self.xSpeed = - abs(self.rect.x - playerCenter[0]) * 1.0 / t
            self.xSpeed /= 5
            self.ySpeed /= 5
        self.image = self.images[self.index]

        # 销毁开关
        self.isDestroy = False

    def landPosition(self, person):
        if person.isStanding:
            if person.direction == Direction.RIGHT:
                if person.isUp:
                    self.rect.x += 10 * PLAYER_SCALE
                    self.rect.y += -1 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 0
                else:
                    self.rect.x += 24 * PLAYER_SCALE
                    self.rect.y += 11 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = 7
            else:
                if person.isUp:
                    self.rect.x += 10 * PLAYER_SCALE
                    self.rect.y += -1 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 0
                else:
                    self.rect.y += 11 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = -7

        elif person.isSquating and not person.isWalking:
            if person.direction == Direction.RIGHT:
                self.rect.x += 34 * PLAYER_SCALE
                self.rect.y += 25 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = 7
            else:
                self.rect.y += 25 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = -7

        elif person.isWalking:
            if person.direction == Direction.RIGHT:
                if person.isUp:
                    self.rect.x += 20 * PLAYER_SCALE
                    self.rect.y += -1 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 7
                elif person.isDown:
                    self.rect.x += 21 * PLAYER_SCALE
                    self.rect.y += 20 * PLAYER_SCALE
                    self.ySpeed = 7
                    self.xSpeed = 7
                else:
                    self.rect.x += 24 * PLAYER_SCALE
                    self.rect.y += 11 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = 7
            else:
                if person.isUp:
                    self.rect.x += -3 * PLAYER_SCALE
                    self.rect.y += -1 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = -7
                elif person.isDown:
                    self.rect.x += -3 * PLAYER_SCALE
                    self.rect.y += 20 * PLAYER_SCALE
                    self.ySpeed = 7
                    self.xSpeed = -7
                else:
                    self.rect.y += 11 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = -7

        elif person.isJumping or person.state == State.FALL:
            if person.direction == Direction.RIGHT:
                self.rect.x += 16 * PLAYER_SCALE
                self.rect.y += 8 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = 7
            else:
                self.rect.x += -2 * PLAYER_SCALE
                self.rect.y += 8 * PLAYER_SCALE
                self.ySpeed = 0
                self.xSpeed = -7

    def waterPosition(self, person):
        if person.isStanding:
            if person.direction == Direction.RIGHT:
                if person.isUp:
                    self.rect.x += 14 * PLAYER_SCALE
                    self.rect.y += 7 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 0
                else:
                    self.rect.x += 27 * PLAYER_SCALE
                    self.rect.y += 29 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = 7
            else:
                if person.isUp:
                    self.rect.x += 7 * PLAYER_SCALE
                    self.rect.y += 3 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 0
                else:
                    self.rect.x += -1 * PLAYER_SCALE
                    self.rect.y += 29 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = -7

        elif person.isWalking:
            if person.direction == Direction.RIGHT:
                if person.isUp:
                    self.rect.x += 23 * PLAYER_SCALE
                    self.rect.y += 17 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = 7
                else:
                    self.rect.x += 27 * PLAYER_SCALE
                    self.rect.y += 29 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = 7
            else:
                if person.isUp:
                    self.rect.x += -3 * PLAYER_SCALE
                    self.rect.y += -1 * PLAYER_SCALE
                    self.ySpeed = -7
                    self.xSpeed = -7
                else:
                    self.rect.x += -1 * PLAYER_SCALE
                    self.rect.y += 29 * PLAYER_SCALE
                    self.ySpeed = 0
                    self.xSpeed = -7

    def move(self):
        self.rect.x += self.xSpeed
        self.rect.y += self.ySpeed
        self.checkBullet()

    def draw(self, window):
        window.blit(self.image, self.rect)

    def checkBullet(self):
        toDestroy = False
        if self.rect.top < 0 or self.rect.top > 600:
            toDestroy = True
        if self.rect.left < 0 or self.rect.right > 900:
            toDestroy = True
        if toDestroy:
            self.isDestroy = True

    def collideEnemy(self, enemyList, explodeList):
        for enemy in enemyList:
            if pygame.sprite.collide_rect(self, enemy):
               self.isDestroy = True
               enemy.isDestroy = True
               explodeList.append(Explode(enemy))

    def collidePlayer(self, player, explodeList):
        if pygame.sprite.collide_rect(self, player):
            # 蹲下的时候,由于图片上半部分是空白,所以子弹必须击中下半部分,才判断为玩家被击中
            if player.isDown or player.isSquating:
                x = player.rect.x
                y = player.rect.y + player.rect.height / 2 + 5
                if (x < self.rect.x < player.rect.x + player.rect.width) and (y < self.rect.y < player.rect.y + player.rect.height):
                    if player.damage(1):
                        self.isDestroy = True
                        explodeList.append(Explode(player, ExplodeVariety.PLAYER1))
                        return True
            elif player.isInWater:
                x = player.rect.x
                y = player.rect.y + player.rect.height / 2
                if (x < self.rect.x < player.rect.x + player.rect.width) and (
                        y < self.rect.y < player.rect.y + player.rect.height):
                    if player.damage(1):
                        self.isDestroy = True
                        explodeList.append(Explode(player, ExplodeVariety.PLAYER1))
                        return True
            else:
                if player.damage(1):
                    self.isDestroy = True
                    explodeList.append(Explode(player, ExplodeVariety.PLAYER1))
                    return True
        return False

Guess you like

Origin blog.csdn.net/qq_37354060/article/details/129756649