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
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
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
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
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
Change the initial velocity of the bullet to 0
Modify constructor parameters
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
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
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
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
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.
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
Let's run the game again to see the problem
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
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
Ok, let's run the game again to see the effect
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