Learning Python's Pygame Development Contra (12)

Continue writing Contra

In the last blog learning Python: Pygame Development Contra (11) , we achieved the effect that the enemy hits the player and the player dies, but there is still a problem. Let’s solve it this time and add it later Check the map collision body, adjust the overall code, and prepare for adding new enemies

The following is the material of the picture

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

1. Modify the player's deduction of health

The following is the function that the player encounters the enemy bullet. We see that the reduction of the player's life value is to modify the value of the variable

insert image description here
We modify the code and write the code for deducting the player's life into a function

Add function in player class

def damage(self, damage):
    if not self.isInvincible:
        self.life -= damage
        return True
    return False

add member variable

self.isInvincible = True

insert image description here
self.isInvincibleThe variable is used to let the player have invincible time. When the player is revived and before landing, it should be invincible, so as to prevent the player from being hit by a bullet and dying just after the player is revived. The damage function is used to deduct the player's life value, and the return value indicates whether to
insert image description here
deduct Minus success

We come to the main class and call the damage() function

Enter the updatePlayerPosition() function and find the position in the red box below
insert image description here
Modify the code

# 与敌人碰撞
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)

insert image description here
Then come to the bullet class and modify the collidePlayer() function

def collidePlayer(self, player, explodeList):
    # 函数的返回值用来表示是否要重新初始化玩家
    # 如果当前子弹和玩家发生碰撞
    if pygame.sprite.collide_rect(self, player):
        if player.damage(1):
            self.isDestroy = True
            explodeList.append(Explode(player, ExplodeVariety.PLAYER1))
            return True
    return False

Just now we set the isInvincible variable. When running the game at this time, the player must be invincible, because the initial value of isInvincible is True, we make the player not invincible when he lands

Enter the updatePlayerPosition() function and add the following code

if MainGame.player1.isInvincible:
    # 玩家落地不无敌
    MainGame.player1.isInvincible = False

insert image description here

Ok, we have finished the modification, run it to see if there is any problem

insert image description here

It runs successfully, no problem

2. Solve the problem that the player's squatting bullet will not hit the player but directly kill the player

When we crouch, the bullet will not fly over the player, but it will be judged that the player has been hit

The reason for this is that the player's picture is very large, and it is necessary to judge when judging the collision. If the player is squatting, it is necessary to calculate whether the bullet has entered the range below the player's center.

We come to the bullet class and modify the colliderPlayer function

insert image description here

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

Now let's run it again to see the effect.
insert image description here
We can see that when the player crouches, the bullet will pass over the player.

3. Improve the map

Next, let's improve the map collision body and modify the main class function initLand()

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)

This picture is the collision volume picture

insert image description here
Let's run it again now

insert image description here
We can see that there are now land colliders behind the map, continuing until the boss

4. Increase the function of generating enemies to solve the problem of always generating enemies

In the current code, we write the logic of generating enemies directly in the update() function. For the convenience of management, we propose the code and write it as a function

insert image description here
Take the code circled in the red box above and write it as a function

insert image description here
However, we have defined the generateEnemy() function before

insert image description here
We renamed the original generateEnemy() function to generateEnemy1(), indicating that the creation is enemy 1, and turned it into a global function

insert image description here

generateEnemy() indicates that all enemies are created

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

After adding member variables

self.enemyBoolList = [True for _ in range(5)]

insert image description here
This array is used to specify the number of enemies generated. The 5 written here means that there are five positions to generate enemies.

So why have this array

In the original enemy spawn code

if -1505 < self.backRect.x < -1500:
        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())

We can see that when the player enters this range (actually the map moves to this range), enemies will be generated, but if the player stops moving after moving to this range, enemies will always be generated, which is obviously not ours What we want to see is that when the player walks within the range, enemies will be generated, and the code will only be executed once, but according to the original logic, if the player has been standing within the range, the code will always be executed, so we have to set an enemyBoolList , if it is True, the code is executed, otherwise it is not executed

The modified code for generating enemies is as follows:insert image description here

Here, the code that only executes once to generate the enemy is set, and the length of the list indicates the number of enemy generation points

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())

Let's run the game and see if there is any problem

insert image description here
ok, everything is fine

5. Add the method of calculating the player center to the player class

The bullet fired by enemy 2 has a tracking effect, so we need to calculate the player's center at all times

We add code to the player class

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

This code is to calculate the player's center position

y0 is the offset of the center, we set it in Constants.py

insert image description here
At this point, it's over, the next step is to create enemy 2

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


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.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)


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)


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

    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())


    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 Player class code


from Constants import *
from Bullet import Bullet


class PlayerOne(pygame.sprite.Sprite):

    def __init__(self, currentTime, life):
        pygame.sprite.Sprite.__init__(self)
        # 加载角色图片
        self.standRightImage = loadImage('../Image/Player/Player1/Right/stand.png')
        self.standLeftImage = loadImage('../Image/Player/Player1/Left/stand.png')
        self.upRightImage = loadImage('../Image/Player/Player1/Up/upRight(small).png')
        self.upLeftImage = loadImage('../Image/Player/Player1/Up/upLeft(small).png')
        self.downRightImage = loadImage('../Image/Player/Player1/Down/down.png')
        self.downLeftImage = loadImage('../Image/Player/Player1/Down/down.png', True)
        self.obliqueUpRightImages = [
            loadImage('../Image/Player/Player1/Up/rightUp1.png'),
            loadImage('../Image/Player/Player1/Up/rightUp2.png'),
            loadImage('../Image/Player/Player1/Up/rightUp3.png'),
        ]
        self.obliqueUpLeftImages = [
            loadImage('../Image/Player/Player1/Up/rightUp1.png', True),
            loadImage('../Image/Player/Player1/Up/rightUp2.png', True),
            loadImage('../Image/Player/Player1/Up/rightUp3.png', True),
        ]
        self.obliqueDownRightImages = [
            loadImage('../Image/Player/Player1/ObliqueDown/1.png'),
            loadImage('../Image/Player/Player1/ObliqueDown/2.png'),
            loadImage('../Image/Player/Player1/ObliqueDown/3.png'),
        ]
        self.obliqueDownLeftImages = [
            loadImage('../Image/Player/Player1/ObliqueDown/1.png', True),
            loadImage('../Image/Player/Player1/ObliqueDown/2.png', True),
            loadImage('../Image/Player/Player1/ObliqueDown/3.png', True),
        ]
        # 角色向右的全部图片
        self.rightImages = [
            loadImage('../Image/Player/Player1/Right/run1.png'),
            loadImage('../Image/Player/Player1/Right/run2.png'),
            loadImage('../Image/Player/Player1/Right/run3.png')
        ]
        # 角色向左的全部图片
        self.leftImages = [
            loadImage('../Image/Player/Player1/Left/run1.png'),
            loadImage('../Image/Player/Player1/Left/run2.png'),
            loadImage('../Image/Player/Player1/Left/run3.png')
        ]
        # 角色跳跃的全部图片
        self.upRightImages = [
            loadImage('../Image/Player/Player1/Jump/jump1.png'),
            loadImage('../Image/Player/Player1/Jump/jump2.png'),
            loadImage('../Image/Player/Player1/Jump/jump3.png'),
            loadImage('../Image/Player/Player1/Jump/jump4.png'),
        ]
        self.upLeftImages = [
            loadImage('../Image/Player/Player1/Jump/jump1.png', True),
            loadImage('../Image/Player/Player1/Jump/jump2.png', True),
            loadImage('../Image/Player/Player1/Jump/jump3.png', True),
            loadImage('../Image/Player/Player1/Jump/jump4.png', True),
        ]
        self.rightFireImages = [
            loadImage('../Image/Player/Player1/Right/fire1.png'),
            loadImage('../Image/Player/Player1/Right/fire2.png'),
            loadImage('../Image/Player/Player1/Right/fire3.png'),
        ]
        self.leftFireImages = [
            loadImage('../Image/Player/Player1/Right/fire1.png', True),
            loadImage('../Image/Player/Player1/Right/fire2.png', True),
            loadImage('../Image/Player/Player1/Right/fire3.png', True),
        ]
        # 加载玩家在水中的图片
        self.upRightImageInWater = loadImage('../Image/Player/Player1/Water/up.png')
        self.upLeftImageInWater = loadImage('../Image/Player/Player1/Water/up.png', True)
        self.diveRightImageInWater = loadImage('../Image/Player/Player1/Water/dive.png')
        self.diveLeftImageInWater = loadImage('../Image/Player/Player1/Water/dive.png', True)
        self.standRightImageInWater = loadImage('../Image/Player/Player1/Water/stand.png')
        self.standLeftImageInWater = loadImage('../Image/Player/Player1/Water/stand.png', True)
        self.fireRightInWater = loadImage('../Image/Player/Player1/Water/standFire.png')
        self.fireLeftInWater = loadImage('../Image/Player/Player1/Water/standFire.png', True)
        self.obliqueRightInWater = loadImage('../Image/Player/Player1/Water/obliqueRight.png')
        self.obliqueLeftInWater = loadImage('../Image/Player/Player1/Water/obliqueRight.png', True)
        self.rightInWaterImage = loadImage('../Image/Player/Player1/Water/inWater.png')
        self.leftInWaterImage = loadImage('../Image/Player/Player1/Water/inWater.png', True)
        # 角色左右移动下标
        self.imageIndex = 0
        # 角色跳跃下标
        self.upImageIndex = 0
        # 角色斜射下标
        self.obliqueImageIndex = 0
        # 上一次显示图片的时间
        self.runLastTimer = currentTime
        self.fireLastTimer = currentTime

        # 选择当前要显示的图片
        self.image = self.standRightImage
        # 获取图片的rect
        self.rect = self.image.get_rect()
        # 设置角色的状态
        self.state = State.FALL
        # 角色的方向
        self.direction = Direction.RIGHT
        # 速度
        self.xSpeed = PLAYER_X_SPEED
        self.ySpeed = 0
        self.jumpSpeed = -11
        # 人物当前的状态标志
        self.isStanding = False
        self.isWalking = False
        self.isJumping = True
        self.isSquating = False
        self.isFiring = False
        self.isInWater = False
        # 重力加速度
        self.gravity = 0.8

        # 玩家上下方向
        self.isUp = False
        self.isDown = False

        self.life = life
        self.isInvincible = True

    def update(self, keys, currentTime, playerBulletList):
        # 更新站或者走的状态
        # 根据状态响应按键
        if self.state == State.STAND:
            self.standing(keys, currentTime, playerBulletList)
        elif self.state == State.WALK:
            self.walking(keys, currentTime, playerBulletList)
        elif self.state == State.JUMP:
            self.jumping(keys, currentTime, playerBulletList)
        elif self.state == State.FALL:
            self.falling(keys, currentTime, playerBulletList)

        # 更新动画
        if self.isInWater:
            self.waterUpdate()
        else:
            self.landUpdate()

    def landUpdate(self):
        # 跳跃状态
        if self.isJumping:
            # 根据方向
            if self.direction == Direction.RIGHT:
                # 方向向右,角色加载向右跳起的图片
                self.image = self.upRightImages[self.upImageIndex]
            else:
                # 否则,方向向左,角色加载向左跳起的图片
                self.image = self.upLeftImages[self.upImageIndex]

        # 角色蹲下
        if self.isSquating:
            if self.direction == Direction.RIGHT:
                # 加载向右蹲下的图片
                self.image = self.downRightImage
            else:
                # 加载向左蹲下的图片
                self.image = self.downLeftImage

        # 角色站着
        if self.isStanding:
            if self.direction == Direction.RIGHT:
                if self.isUp:
                    # 加载向右朝上的图片
                    self.image = self.upRightImage
                elif self.isDown:
                    # 加载向右蹲下的图片
                    self.image = self.downRightImage
                else:
                    # 加载向右站着的图片
                    self.image = self.standRightImage
            else:
                # 向左也是同样的效果
                if self.isUp:
                    self.image = self.upLeftImage
                elif self.isDown:
                    self.image = self.downLeftImage
                else:
                    self.image = self.standLeftImage

        # 角色移动
        if self.isWalking:
            if self.direction == Direction.RIGHT:
                if self.isUp:
                    # 加载斜右上的图片
                    self.image = self.obliqueUpRightImages[self.obliqueImageIndex]
                elif self.isDown:
                    # 加载斜右下的图片
                    self.image = self.obliqueDownRightImages[self.obliqueImageIndex]
                else:
                    # 加载向右移动的图片,根据开火状态是否加载向右开火移动的图片
                    if self.isFiring:
                        self.image = self.rightFireImages[self.imageIndex]
                    else:
                        self.image = self.rightImages[self.imageIndex]
            else:
                if self.isUp:
                    self.image = self.obliqueUpLeftImages[self.obliqueImageIndex]
                elif self.isDown:
                    self.image = self.obliqueDownLeftImages[self.obliqueImageIndex]
                else:
                    if self.isFiring:
                        self.image = self.leftFireImages[self.imageIndex]
                    else:
                        self.image = self.leftImages[self.imageIndex]

    def waterUpdate(self):
        if self.isSquating:
            if self.direction == Direction.RIGHT:
                self.image = self.diveRightImageInWater
            else:
                self.image = self.diveLeftImageInWater

        if self.isStanding:
            if self.direction == Direction.RIGHT:
                if self.isFiring:
                    if self.isUp:
                        self.image = self.upRightImageInWater
                    else:
                        self.image = self.fireRightInWater
                else:
                    if self.isUp:
                        self.image = self.upRightImageInWater
                    else:
                        self.image = self.standRightImageInWater
            else:
                if self.isFiring:
                    if self.isUp:
                        self.image = self.upLeftImageInWater
                    else:
                        self.image = self.fireLeftInWater
                else:
                    if self.isUp:
                        self.image = self.upLeftImageInWater
                    else:
                        self.image = self.standLeftImageInWater

        if self.isWalking:
            if self.direction == Direction.RIGHT:
                if self.isUp:
                    self.image = self.obliqueRightInWater
                else:
                    if self.isFiring:
                        self.image = self.fireRightInWater
                    else:
                        self.image = self.standRightImageInWater
            else:
                if self.isUp:
                    self.image = self.obliqueLeftInWater
                else:
                    if self.isFiring:
                        self.image = self.fireLeftInWater
                    else:
                        self.image = self.standLeftImageInWater

    def standing(self, keys, currentTime, playerBulletList):
        """角色站立"""

        # 设置角色状态
        self.isStanding = True
        self.isWalking = False
        self.isJumping = False
        self.isSquating = False
        self.isUp = False
        self.isDown = False
        self.isFiring = False

        # 设置速度
        self.ySpeed = 0
        self.xSpeed = 0

        # 按下A键
        if keys[pygame.K_a]:
            # A按下,角色方向向左
            self.direction = Direction.LEFT
            # 改变角色的状态,角色进入移动状态
            self.state = State.WALK
            # 设置站立状态为False,移动状态为True
            self.isStanding = False
            self.isWalking = True
            # 向左移动,速度为负数,这样玩家的x坐标是减小的
            self.xSpeed = -PLAYER_X_SPEED
        # 按下D键
        elif keys[pygame.K_d]:
            # D按下,角色方向向右
            self.direction = Direction.RIGHT
            # 改变角色的状态,角色进入移动状态
            self.state = State.WALK
            # 设置站立状态为False,移动状态为True
            self.isStanding = False
            self.isWalking = True
            # 向右移动,速度为正数
            self.xSpeed = PLAYER_X_SPEED
        # 按下k键
        elif keys[pygame.K_k]:
            if not self.isInWater:
                # K按下,角色进入跳跃状态,但是不会改变方向
                self.state = State.JUMP
                # 设置站立状态为False,跳跃状态为True
                # 不改变移动状态,因为移动的时候也可以跳跃
                self.isStanding = False
                self.isJumping = True
                # 设置速度,速度为负数,因为角色跳起后,要下落
                self.isUp = True
                self.ySpeed = self.jumpSpeed
        # 没有按下按键
        else:
            # 没有按下按键,角色依然是站立状态
            self.state = State.STAND
            self.isStanding = True

        # 按下w键
        if keys[pygame.K_w]:
            # W按下,角色向上,改变方向状态
            self.isUp = True
            self.isStanding = True
            self.isDown = False
            self.isSquating = False
        # 按下s键
        elif keys[pygame.K_s]:
            # S按下,角色蹲下,改变方向状态,并且蹲下状态设置为True
            self.isUp = False
            self.isStanding = False
            self.isDown = True
            self.isSquating = True

        if keys[pygame.K_j]:
            self.fire(currentTime, playerBulletList)

    def walking(self, keys, currentTime, playerBulletList):
        """角色行走,每10帧变换一次图片"""
        self.isStanding = False
        self.isWalking = True
        self.isJumping = False
        self.isSquating = False
        self.isFiring = False
        self.ySpeed = 0
        self.xSpeed = PLAYER_X_SPEED

        if self.isInWater:
            self.walkingInWater(currentTime)
        else:
            self.walkingInLand(currentTime)

        # 按下D键
        if keys[pygame.K_d]:
            self.direction = Direction.RIGHT
            self.xSpeed = PLAYER_X_SPEED
        # 按下A键
        elif keys[pygame.K_a]:
            self.direction = Direction.LEFT
            self.xSpeed = -PLAYER_X_SPEED
         # 按下S键
        elif keys[pygame.K_s]:
            self.isStanding = False
            self.isDown = True
            self.isUp = False

        # 按下W键
        if keys[pygame.K_w]:
            self.isUp = True
            self.isDown = False
        # 没有按键按下
        else:
            self.state = State.STAND

        # 移动时按下K键
        if keys[pygame.K_k]:
            # 角色状态变为跳跃
            if not self.isInWater:
                self.state = State.JUMP
                self.ySpeed = self.jumpSpeed
                self.isJumping = True
                self.isStanding = False
                self.isUp = True

        if keys[pygame.K_j]:
            self.fire(currentTime, playerBulletList)

    def walkingInLand(self, currentTime):
        # 如果当前是站立的图片
        if self.isStanding:
            # 方向向右,方向向上
            if self.direction == Direction.RIGHT and self.isUp:
                # 设置为向右朝上的图片
                self.image = self.upRightImage
            # 方向向右
            elif self.direction == Direction.RIGHT and not self.isUp:
                # 设置为向右站立的图片
                self.image = self.standRightImage
            elif self.direction == Direction.LEFT and self.isUp:
                self.image = self.upLeftImage
            elif self.direction == Direction.LEFT and not self.isUp:
                self.image = self.standLeftImage
            # 记下当前时间
            self.runLastTimer = currentTime
        else:
            # 如果是走动的图片,先判断方向
            if self.direction == Direction.RIGHT:
                # 设置速度
                self.xSpeed = PLAYER_X_SPEED
                # 根据上下方向觉得是否角色要加载斜射的图片
                if self.isUp or self.isDown:
                    # isUp == True表示向上斜射
                    # isDown == True表示向下斜射
                    # 计算上一次加载图片到这次的时间,如果大于115,即11.5帧,即上次加载图片到这次加载图片之间,已经加载了11张图片
                    if currentTime - self.runLastTimer > 115:
                        # 那么就可以加载斜着奔跑的图片
                        # 如果角色加载的图片不是第三张,则加载下一张就行
                        if self.obliqueImageIndex < 2:
                            self.obliqueImageIndex += 1
                        # 否则就加载第一张图片
                        else:
                            self.obliqueImageIndex = 0
                        # 记录变换图片的时间,为下次变换图片做准备
                        self.runLastTimer = currentTime
                # 不是斜射
                else:
                    # 加载正常向右奔跑的图片
                    if currentTime - self.runLastTimer > 115:
                        if self.imageIndex < 2:
                            self.imageIndex += 1
                        else:
                            self.imageIndex = 0
                        self.runLastTimer = currentTime
            else:
                self.xSpeed = -PLAYER_X_SPEED
                if self.isUp or self.isDown:
                    if currentTime - self.runLastTimer > 115:
                        if self.obliqueImageIndex < 2:
                            self.obliqueImageIndex += 1
                        else:
                            self.obliqueImageIndex = 0
                        self.runLastTimer = currentTime
                else:
                    if currentTime - self.runLastTimer > 115:
                        if self.imageIndex < 2:
                            self.imageIndex += 1
                        else:
                            self.imageIndex = 0
                        self.runLastTimer = currentTime

    def walkingInWater(self, currentTime):
        if self.isStanding:
            # 设置为斜射
            if self.direction == Direction.RIGHT and self.isUp:
                self.image = self.upRightImageInWater
            elif self.direction == Direction.RIGHT and not self.isUp:
                self.image = self.standRightImageInWater
            elif self.direction == Direction.LEFT and self.isUp:
                self.image = self.upLeftImageInWater
            elif self.direction == Direction.LEFT and not self.isUp:
                self.image = self.standLeftImageInWater
            self.runLastTimer = currentTime
        else:
            # 如果是走动的图片
            if self.direction == Direction.RIGHT:
                self.xSpeed = PLAYER_X_SPEED
                if self.isUp:
                    self.image = self.obliqueRightInWater
                    self.runLastTimer = currentTime
                else:
                    self.image = self.standRightImageInWater
                    self.runLastTimer = currentTime
            else:
                self.xSpeed = PLAYER_X_SPEED
                if self.isUp:
                    self.image = self.obliqueLeftInWater
                    self.runLastTimer = currentTime
                else:
                    self.image = self.standLeftImageInWater
                    self.runLastTimer = currentTime

    def jumping(self, keys, currentTime, playerBulletList):
        """跳跃"""
        # 设置标志
        self.isJumping = True
        self.isStanding = False
        self.isDown = False
        self.isSquating = False
        self.isFiring = False
        # 更新速度
        self.ySpeed += self.gravity
        if currentTime - self.runLastTimer > 115:
            if self.upImageIndex < 3:
                self.upImageIndex += 1
            else:
                self.upImageIndex = 0
            # 记录变换图片的时间,为下次变换图片做准备
            self.runLastTimer = currentTime

        if keys[pygame.K_d]:
            self.direction = Direction.RIGHT

        elif keys[pygame.K_a]:
            self.direction = Direction.LEFT

        # 按下W键
        if keys[pygame.K_w]:
            self.isUp = True
            self.isDown = False
        elif keys[pygame.K_s]:
            self.isUp = False
            self.isDown = True

        if self.ySpeed >= 0:
            self.state = State.FALL

        if not keys[pygame.K_k]:
            self.state = State.FALL

        if keys[pygame.K_j]:
            self.fire(currentTime, playerBulletList)

    def falling(self, keys, currentTime, playerBulletList):
        # 下落时速度越来越快,所以速度需要一直增加
        self.ySpeed += self.gravity
        if currentTime - self.runLastTimer > 115:
            if self.upImageIndex < 3:
                self.upImageIndex += 1
            else:
                self.upImageIndex = 0
            self.runLastTimer = currentTime

        if keys[pygame.K_d]:
            self.direction = Direction.RIGHT
            self.isWalking = False

        elif keys[pygame.K_a]:
            self.direction = Direction.LEFT
            self.isWalking = False

        if keys[pygame.K_j]:
            self.fire(currentTime, playerBulletList)

    def fire(self, currentTime, playerBulletList):
        self.isFiring = True
        # 潜水状态下不能开火
        if not (self.isInWater and self.isSquating):
            if len(playerBulletList) < PLAYER_BULLET_NUMBER:
                if currentTime - self.fireLastTimer > 150:
                    playerBulletList.append(Bullet(self))
                    self.fireLastTimer = currentTime

    def damage(self, damage):
        if not self.isInvincible:
            self.life -= damage
            return True
        return False

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

Complete bullet class code

import pygame
from Constants import *
from Explode import Explode


class Bullet(pygame.sprite.Sprite):

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

        if not isEnemy:
            if person.isInWater:
                self.waterPosition(person)
            else:
                self.landPosition(person)
        else:
            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

        # 销毁开关
        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

If there are any problems in the code, please let me know, and I will make changes!

Guess you like

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