目录
- 实现功能
- 用到的编程知识
- 代码如下
- 部分运行截图
实现功能
1:敌人的绵绵不断的前进,拿着各种各样的武器(叉子,斧头,宝剑),挥动武器攻击我方城堡,对我方城堡造成伤害!
2:我方城堡发现敌人可手动点击鼠标左键来发起子弹攻击,对日人造成致命伤害,让其死亡!
3:完备的数据显示,攻击敌人获取金币,累计得分,当前管卡的级别,我方城堡生命值的显示等等,击杀敌人获取的金币可以兑换额外属性来装备回复加强我方堡垒!
4:项目的布局界面优美干净,结合添加的纯音乐游戏背景和攻击音效以及实时的动画显示(如我方城堡的外观会随着我方城堡生命值的降低而发生改变,也就是变得会破败一些等等)以此让项目更加具有可玩性!
5:拿该项目练手或者作为一个python简单的课程设计也是一个可以的选择!
6:项目总代码700行左右
用到的编程知识
python基础,os文件读写,pygame模块以及面向对象思想!
代码如下
enemy.py类文件(100行代码左右)
import pygame | |
class Enemy(pygame.sprite.Sprite): | |
def __init__(self, health, animation_list, x, y, speed): | |
pygame.sprite.Sprite.__init__(self) | |
self.alive = True | |
self.speed = speed | |
self.health = health | |
self.last_attack = pygame.time.get_ticks() | |
self.attack_cooldown = | |
self.animation_list = animation_list | |
self.frame_index = | |
self.action =#0: walk, 1: attack, 2: death | |
self.update_time = pygame.time.get_ticks() | |
#select starting image | |
self.image = self.animation_list[self.action][self.frame_index] | |
self.rect = pygame.Rect(, 0, 25, 40) | |
self.rect.center = (x, y) | |
def update(self, surface, target, bullet_group): | |
if self.alive: | |
#check for collision with bullets | |
if pygame.sprite.spritecollide(self, bullet_group, True): | |
#lower enemy health | |
self.health -= | |
#check if enemy has reached the castle | |
if self.rect.right > target.rect.left: | |
self.update_action() | |
#move enemy | |
if self.action ==: | |
#update rectangle position | |
self.rect.x += self.speed | |
#attack | |
if self.action ==: | |
#check if enough time has passed since last attack | |
if pygame.time.get_ticks() - self.last_attack > self.attack_cooldown: | |
target.health -= | |
if target.health <: | |
target.health = | |
self.last_attack = pygame.time.get_ticks() | |
#check if health has dropped to zero | |
if self.health <=: | |
target.money += | |
target.score += | |
self.update_action()#death | |
self.alive = False | |
self.update_animation() | |
#draw image on screen | |
surface.blit(self.image, (self.rect.x -, self.rect.y - 15)) | |
def update_animation(self): | |
#define animation cooldown | |
ANIMATION_COOLDOWN = | |
#update image depending on current action | |
self.image = self.animation_list[self.action][self.frame_index] | |
#check if enough time has passed since the last update | |
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN: | |
self.update_time = pygame.time.get_ticks() | |
self.frame_index += | |
#if the animation has run out then reset back to the start | |
if self.frame_index >= len(self.animation_list[self.action]): | |
if self.action ==: | |
self.frame_index = len(self.animation_list[self.action]) - | |
else: | |
self.frame_index = | |
def update_action(self, new_action): | |
#check if the new action is different to the previous one | |
if new_action != self.action: | |
self.action = new_action | |
#update the animation settings | |
self.frame_index = | |
self.update_date = pygame.time.get_ticks() |
castle.py类文件(500行代码左右)
# 导入库 | |
import pygame | |
import math | |
import os | |
import sys | |
import random | |
import button | |
from pygame import mixer | |
# 初始化pygame | |
pygame.init() | |
# 定义游戏窗口高度和宽度 | |
SCREEN_WIDTH = | |
SCREEN_HEIGHT = | |
# 加载背景音乐 | |
pygame.mixer.music.load("sound/bjmusic.WAV") | |
pygame.mixer.music.set_volume(.3) | |
jump_fx = pygame.mixer.Sound("sound/bullet.wav") | |
jump_fx.set_volume(.5) | |
# 创建游戏窗口 | |
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) | |
pygame.display.set_caption("城堡防卫战") | |
clock = pygame.time.Clock() | |
FPS = | |
# 定义游戏变量 | |
level = | |
high_score = | |
level_difficulty = | |
target_difficulty = | |
DIFFICULTY_MULTIPLIER =.1 | |
game_over = False | |
next_level = False | |
ENEMY_TIMER = | |
last_enemy = pygame.time.get_ticks() | |
enemies_alive = | |
max_towers = | |
TOWER_COST = | |
# 定义炮塔位置的列表 | |
tower_positions = [ | |
[SCREEN_WIDTH -, SCREEN_HEIGHT - 200], | |
[SCREEN_WIDTH -, SCREEN_HEIGHT - 150], | |
[SCREEN_WIDTH -, SCREEN_HEIGHT - 150], | |
[SCREEN_WIDTH -, SCREEN_HEIGHT - 150] | |
] | |
# 加载最高分 | |
if os.path.exists('socre.txt'): | |
with open('socre.txt', 'r') as file: | |
high_score = int(file.read()) | |
# 定义颜色 | |
WHITE = (, 255, 255) | |
GREY = (, 100, 100) | |
# 定义字体 | |
font = pygame.font.SysFont('华文彩云',) | |
font_ = pygame.font.SysFont('华文行楷', 60) | |
# 加载图片 | |
bg = pygame.image.load('img/bg.png').convert_alpha() | |
# 城堡 | |
castle_img_ = pygame.image.load('img/castle/castle_100.png').convert_alpha() | |
castle_img_ = pygame.image.load('img/castle/castle_50.png').convert_alpha() | |
castle_img_ = pygame.image.load('img/castle/castle_25.png').convert_alpha() | |
# 炮塔 | |
tower_img_ = pygame.image.load('img/tower/tower_100.png').convert_alpha() | |
tower_img_ = pygame.image.load('img/tower/tower_50.png').convert_alpha() | |
tower_img_ = pygame.image.load('img/tower/tower_25.png').convert_alpha() | |
# 子弹图像 | |
bullet_img = pygame.image.load('img/bullet.png').convert_alpha() | |
b_w = bullet_img.get_width() | |
b_h = bullet_img.get_height() | |
bullet_img = pygame.transform.scale(bullet_img, (int(b_w *.075), int(b_h * 0.075))) | |
# 创建敌人类 | |
class Enemy(pygame.sprite.Sprite): | |
def __init__(self, health, animation_list, x, y, speed): | |
super().__init__() | |
self.alive = True | |
self.speed = speed | |
self.health = health | |
self.last_attack = pygame.time.get_ticks() | |
self.attack_cooldown = | |
self.animation_list = animation_list | |
self.frame_index = | |
self.action = | |
self.update_time = pygame.time.get_ticks() | |
# 选择动画开始的图片 | |
self.image = self.animation_list[self.action][self.frame_index] | |
self.rect = pygame.Rect(, 0, 25, 40) | |
self.rect.center = (x, y) | |
def update(self, surface, target, bullet_group): | |
if self.alive: | |
# 检查敌人与子弹的碰撞 | |
if pygame.sprite.spritecollide(self, bullet_group, True): | |
# 减少健康 | |
self.health -= | |
# 检查敌人是否已经到达城堡 | |
if self.rect.right > target.rect.left: | |
self.update_action() | |
# 移动敌人 | |
if self.action ==: | |
self.rect.x += | |
# 攻击城堡 | |
if self.action ==: | |
# 检测冷却时间 | |
if pygame.time.get_ticks() - self.last_attack > self.attack_cooldown: | |
target.health -= | |
if target.health <: | |
target.health = | |
self.last_attack = pygame.time.get_ticks() | |
# 检查敌人血条是否为 | |
if self.health <=: | |
target.money += | |
target.score += | |
self.update_action() | |
self.alive = False | |
# 调用更新动画 | |
self.update_animation() | |
# 绘制敌人 | |
# pygame.draw.rect(surface, (, 255, 255), self.rect, 1) | |
surface.blit(self.image, (self.rect.x -, self.rect.y - 15)) | |
def update_animation(self): | |
# 定义动画冷却时间 | |
ANIMATION_COOLDOWN = | |
# 根据选择的冬瓜更新帧 | |
self.image = self.animation_list[self.action][self.frame_index] | |
# 判断多久更新一次帧 | |
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN: | |
self.update_time = pygame.time.get_ticks() | |
self.frame_index += | |
# 检查帧数不能超过最大帧数 | |
if self.frame_index >= len(self.animation_list[self.action]): | |
if self.action ==: | |
self.frame_index = len(self.animation_list[self.action]) - | |
else: | |
self.frame_index = | |
def update_action(self, new_action): | |
# 检查新动作与上一个动作是否相同 | |
if new_action != self.action: | |
self.action = new_action | |
# 更新动画重置 | |
self.frame_index = | |
self.update_time = pygame.time.get_ticks() | |
# 加载敌人列表 | |
enemy_animations = [] | |
enemy_tpyes = ['knight', 'goblin', 'purple_goblin', 'red_goblin'] | |
enemy_health = [, 100, 125, 150] | |
animation_types = ['walk', 'attack', 'death'] | |
for enemy in enemy_tpyes: | |
# 加载动画列表 | |
animation_list = [] | |
for animation in animation_types: | |
# 创建临时列表 | |
temp_list = [] | |
# 定义帧数 | |
num_of_frames = | |
for i in range(num_of_frames): | |
img = pygame.image.load(f'img/enemies/{enemy}/{animation}/{i}.png').convert_alpha() | |
e_w = img.get_width() | |
e_h = img.get_height() | |
img = pygame.transform.scale(img, (int(e_w *.2), int(e_h * 0.2))) | |
temp_list.append(img) | |
animation_list.append(temp_list) | |
enemy_animations.append(animation_list) | |
# 加载按钮图片 | |
repair_img = pygame.image.load('img/repair.png').convert_alpha() | |
armour_img = pygame.image.load('img/armour.png').convert_alpha() | |
# 在屏幕上输出文本信息 | |
def draw_text(text, font, text_color, x, y): | |
img = font.render(text, True, text_color) | |
screen.blit(img, (x, y)) | |
# 定义一个显示状态的函数 | |
def show_info(): | |
draw_text('钱数:' + str(castle.money), font, GREY,, 10) | |
draw_text('分数:' + str(castle.score), font, GREY,, 10) | |
draw_text('最分数:' + str(high_score), font, GREY,, 50) | |
draw_text('级别:' + str(level), font, GREY, SCREEN_WIDTH //, 10) | |
draw_text('健康:' + str(castle.health) + "/" + str(castle.max_health), font, GREY, SCREEN_WIDTH -, SCREEN_HEIGHT - 50) | |
draw_text('', font, GREY, SCREEN_WIDTH - 250, 70) | |
draw_text(str(TOWER_COST), font, GREY, SCREEN_WIDTH -, 70) | |
draw_text('', font, GREY, SCREEN_WIDTH - 70, 70) | |
# 城堡类 | |
class Castle(): | |
def __init__(self, image, image50, image25, x, y, scale): | |
self.health = | |
self.max_health = self.health | |
self.fired = False | |
self.money = | |
self.score = | |
width = image.get_width() | |
height = image.get_height() | |
self.image = pygame.transform.scale(image100, (int(width * scale), int(height * scale))) | |
self.image = pygame.transform.scale(image50, (int(width * scale), int(height * scale))) | |
self.image = pygame.transform.scale(image25, (int(width * scale), int(height * scale))) | |
self.rect = self.image.get_rect() | |
self.rect.x = x | |
self.rect.y = y | |
def shoot(self): | |
pos = pygame.mouse.get_pos() | |
x_dist = pos[] - self.rect.midleft[0] | |
y_dist = -(pos[] - self.rect.midleft[1]) | |
self.angle = math.degrees(math.atan(y_dist, x_dist)) | |
# 在该位置点击鼠标 | |
if pygame.mouse.get_pressed()[] and self.fired == False and pos[1] > 70: | |
self.fired = True | |
bullet = Bullet(bullet_img, self.rect.midleft[], self.rect.midleft[1], self.angle) | |
bullet_group.add(bullet) | |
jump_fx.play() | |
# 重置鼠标点击 | |
if pygame.mouse.get_pressed()[] == False: | |
self.fired = False | |
def draw(self): | |
# 根据血量判断加载那张图片 | |
if self.health <=: | |
self.image = self.image | |
elif self.health <=: | |
self.image = self.image | |
else: | |
self.image = self.image | |
screen.blit(self.image, self.rect) | |
def repair(self): | |
if self.money >= and self.health < self.max_health: | |
self.health += | |
self.money -= | |
if castle.health > castle.max_health: | |
castle.health = castle.max_health | |
def armour(self): | |
if self.money >=: | |
self.max_health += | |
self.money -= | |
# 炮塔类 | |
class Tower(pygame.sprite.Sprite): | |
def __init__(self, image, image50, image25, x, y, scale): | |
super().__init__() | |
self.got_target = False | |
self.angle = | |
self.last_shot = pygame.time.get_ticks() | |
width = image.get_width() | |
height = image.get_height() | |
self.image = pygame.transform.scale(image100, (int(width * scale), int(height * scale))) | |
self.image = pygame.transform.scale(image50, (int(width * scale), int(height * scale))) | |
self.image = pygame.transform.scale(image25, (int(width * scale), int(height * scale))) | |
self.image = self.image | |
self.rect = self.image.get_rect() | |
self.rect.x = x | |
self.rect.y = y | |
def update(self, enemy_group): | |
self.got_target = False | |
for e in enemy_group: | |
if e.alive: | |
target_x, target_y = e.rect.midbottom | |
self.got_target = True | |
break | |
if self.got_target: | |
x_dist = target_x - self.rect.midleft[] | |
y_dist = -(target_y - self.rect.midleft[]) | |
self.angle = math.degrees(math.atan(y_dist, x_dist)) | |
# pygame.draw.line(screen, WHITE, (self.rect.midleft[], self.rect.midleft[1]), (target_x, target_y)) | |
shot_cooldown = | |
# 开火 | |
if pygame.time.get_ticks() - self.last_shot > shot_cooldown: | |
self.last_shot = pygame.time.get_ticks() | |
bullet = Bullet(bullet_img, self.rect.midleft[], self.rect.midleft[1], self.angle) | |
bullet_group.add(bullet) | |
# 根据城堡血量判断加载那张图片 | |
if castle.health <=: | |
self.image = self.image | |
elif castle.health <=: | |
self.image = self.image | |
else: | |
self.image = self.image | |
# 创建子弹类 | |
class Bullet(pygame.sprite.Sprite): | |
def __init__(self, image, x, y, angle): | |
super().__init__() | |
self.image = image | |
self.rect = self.image.get_rect() | |
self.rect.x = x | |
self.rect.y = y | |
self.angle = math.radians(angle) # 角度转换为弧度 | |
self.speed = | |
# 根据角度计算水平和垂直的速度 | |
self.dx = math.cos(self.angle) * self.speed | |
self.dy = -(math.sin(self.angle) * self.speed) | |
def update(self): | |
# 检测子弹是否已经超出窗口 | |
if self.rect.right < or self.rect.left > SCREEN_WIDTH or self.rect.bottom < 0 or self.rect.top > SCREEN_HEIGHT: | |
self.kill() | |
# 移动子弹 | |
self.rect.x += self.dx | |
self.rect.y += self.dy | |
# 创建十字准心 | |
class Crosshair(): | |
def __init__(self, scale): | |
image = pygame.image.load("img/crosshair.png").convert_alpha() | |
width = image.get_width() | |
height = image.get_height() | |
self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale))) | |
self.rect = self.image.get_rect() | |
# 隐藏鼠标指针 | |
pygame.mouse.set_visible(False) | |
def draw(self): | |
mx, my = pygame.mouse.get_pos() | |
self.rect.center = (mx, my) | |
screen.blit(self.image, self.rect) | |
# 创建城堡 | |
castle = Castle(castle_img_, castle_img_50, castle_img_25, SCREEN_WIDTH - 250, SCREEN_HEIGHT - 300, 0.2) | |
# 实例化十字准心 | |
crosshair = Crosshair(.025) | |
# 创建按钮 | |
repair_button = button.Button(SCREEN_WIDTH -, 10, repair_img, 0.5) | |
tower_button = button.Button(SCREEN_WIDTH -, 10, tower_img_100, 0.1) | |
armour_button = button.Button(SCREEN_WIDTH -, 10, armour_img, 1.5) | |
# 创建组 | |
bullet_group = pygame.sprite.Group() | |
enemy_group = pygame.sprite.Group() | |
tower_group = pygame.sprite.Group() | |
# 穿件临时塔 | |
# tower = Tower(tower_img_, tower_img_50, tower_img_25, SCREEN_WIDTH - 350, SCREEN_HEIGHT - 200, 0.2) | |
# tower_group.add(tower) | |
# 游戏循环显示窗口 | |
pygame.mixer.music.unpause() | |
pygame.mixer.music.play(-) | |
run = True | |
while run: | |
clock.tick(FPS) | |
if game_over == False: | |
screen.blit(bg, (, 0)) | |
# 显示城堡 | |
castle.draw() | |
castle.shoot() | |
# 显示炮塔 | |
tower_group.draw(screen) | |
tower_group.update(enemy_group) | |
# 显示十字准心 | |
crosshair.draw() | |
# 绘制子弹到屏幕 | |
bullet_group.update() | |
bullet_group.draw(screen) | |
# 绘制敌人 | |
enemy_group.update(screen, castle, bullet_group) | |
# 显示详细信息 | |
show_info() | |
# 显示按钮 修理和铠甲按钮 | |
if repair_button.draw(screen): | |
castle.repair() | |
if tower_button.draw(screen): | |
# 检查是否有足够的金钱来建造炮塔 | |
if castle.money >= TOWER_COST and len(tower_group) < max_towers: | |
tower = Tower(tower_img_, | |
tower_img_, | |
tower_img_, | |
tower_positions[len(tower_group)][], | |
tower_positions[len(tower_group)][], | |
.2) | |
tower_group.add(tower) | |
# 减去消耗的金钱数 | |
castle.money -= TOWER_COST | |
if armour_button.draw(screen): | |
castle.armour() | |
# 创建不同的敌人 | |
if level_difficulty < target_difficulty: | |
if pygame.time.get_ticks() - last_enemy > ENEMY_TIMER: | |
# 创建敌人实例 | |
e = random.randint(, len(enemy_tpyes) - 1) | |
enemy = Enemy(enemy_health[e], enemy_animations[e], -, SCREEN_HEIGHT - 100, 1) | |
enemy_group.add(enemy) | |
last_enemy = pygame.time.get_ticks() | |
level_difficulty += enemy_health[e] | |
# 检测是所有的的敌人都产生了 | |
if level_difficulty >= target_difficulty: | |
# 检查有多少敌人仍然是活着的 | |
enemies_alive = | |
for e in enemy_group: | |
if e.alive == True: | |
enemies_alive += | |
# 检测如果活着的敌人都被消灭了则当前级别就完成了 | |
if enemies_alive == and next_level == False: | |
next_level = True | |
level_reset_time = pygame.time.get_ticks() | |
# 判断是否进入下一关 | |
if next_level == True: | |
draw_text('关卡已完成', font_, WHITE, 200, 300) | |
# 更新最高分 | |
if castle.score > high_score: | |
high_score = castle.score | |
with open('socre.txt', 'w') as file: | |
file.write(str(high_score)) | |
if pygame.time.get_ticks() - level_reset_time >: | |
next_level = False | |
level += | |
last_enemy = pygame.time.get_ticks() | |
target_difficulty *= DIFFICULTY_MULTIPLIER | |
level_difficulty = | |
enemy_group.empty() | |
# 检查游戏是否结束 | |
if castle.health <=: | |
game_over = True | |
else: | |
draw_text('游戏结束!', font, GREY,, 300) | |
draw_text('按下"A"重新进入游戏', font, GREY,, 350) | |
pygame.mouse.set_visible(True) | |
key = pygame.key.get_pressed() | |
if key[pygame.K_a]: | |
# 重置游戏 | |
game_over = False | |
level = | |
target_difficulty = | |
level_difficulty = | |
last_enemy = pygame.time.get_ticks() | |
enemy_group.empty() | |
tower_group.empty() | |
castle.score = | |
castle.health = | |
castle.max_health = castle.health | |
castle.money = | |
pygame.mouse.set_visible(False) | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
run = False | |
pygame.quit() | |
sys.exit() | |
pygame.display.update() |
button.py类文件(50行代码左右)
import pygame | |
# 按钮类 | |
class Button(): | |
def __init__(self, x, y, image, scale): | |
width = image.get_width() | |
height = image.get_height() | |
self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale))) | |
self.rect = self.image.get_rect() | |
self.rect.topleft = (x, y) | |
self.clicked = False | |
def draw(self, surface): | |
action = False | |
# 得到鼠标的位置 | |
pos = pygame.mouse.get_pos() | |
# 检测鼠标指针的碰撞 | |
if self.rect.collidepoint(pos): | |
if pygame.mouse.get_pressed()[] == 1 and self.clicked == False: | |
self.clicked = True | |
action = True | |
if pygame.mouse.get_pressed()[] == 0: | |
self.clicked = False | |
# 画按钮到屏幕上 | |
surface.blit(self.image, (self.rect.x, self.rect.y)) | |
return action |