鱼C论坛

 找回密码
 立即注册
查看: 65|回复: 7

[作品展示] 一个简单的塔防游戏

[复制链接]
发表于 2026-8-28 08:15:47 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. # -*- coding: utf-8 -*-
  2. """
  3. ================================================================================
  4. 塔防游戏
  5. ================================================================================
  6. 操作说明:
  7.   方向键 / WASD : 移动主角色(红色正方形)
  8.   鼠标左键      : 在地图上放置红色三角标,小兵会围绕三角标行动
  9.   鼠标右键      : 取消三角标
  10.   T 键          : 在主角色当前位置建造一座塔(红色正方形,自动攻击怪物)
  11.   1 键          : 生成一支近战小兵(白色正方形)
  12.   2 键          : 生成一支远程小兵(白色圆形)
  13.   R 键          : 游戏结束后重新开始
  14.   ESC 键        : 退出游戏
  15. ================================================================================
  16. """

  17. # 导入 pygame 库,用于创建窗口、绘制图形、处理事件等
  18. import pygame
  19. # 从 pygame.locals 导入常用常量(如按键、事件类型等)
  20. from pygame.locals import *
  21. # 导入 random 模块,用于生成随机数(如怪物生成间隔、随机偏移等)
  22. import random
  23. # 导入 math 模块,用于数学计算(如两点间距离、三角函数等)
  24. import math
  25. # 导入 sys 模块,用于系统相关操作(如退出程序)
  26. import sys


  27. # ==============================================================================
  28. # 全局常量定义
  29. # ==============================================================================

  30. # 屏幕宽度(像素)
  31. SCREEN_WIDTH = 1000
  32. # 屏幕高度(像素)
  33. SCREEN_HEIGHT = 700

  34. # 游戏刷新帧率(每秒多少帧)
  35. FPS = 60

  36. # 颜色定义(RGB 格式)
  37. COLOR_WHITE = (255, 255, 255)   # 白色 - 近战小兵(正方形) / 远程小兵(圆形)
  38. COLOR_BLACK = (0, 0, 0)         # 黑色 - 近战怪物(正方形) / 远程怪物(圆形)
  39. COLOR_RED = (220, 30, 30)       # 红色 - 主角色(正方形) / 塔(正方形)
  40. COLOR_RED_DARK = (160, 20, 20)  # 深红色 - 主角色子弹(小正方形)
  41. COLOR_GRAY = (120, 120, 120)    # 灰色 - UI 文字、路径辅助线
  42. COLOR_GREEN = (40, 200, 40)     # 绿色 - 生命条(健康)
  43. COLOR_YELLOW = (230, 210, 30)   # 黄色 - 三角标
  44. COLOR_BG = (30, 30, 40)         # 背景色 - 深蓝灰

  45. # 主角色移动速度(像素/秒)
  46. HERO_SPEED = 220
  47. # 塔的攻击范围(像素)
  48. TOWER_RANGE = 220
  49. # 塔的射击冷却时间(毫秒)
  50. TOWER_COOLDOWN = 700

  51. # 小兵围绕目标(主角色/三角标)的半径范围
  52. SOLDIER_ORBIT_MIN = 60
  53. SOLDIER_ORBIT_MAX = 130

  54. # 远程单位射击冷却(毫秒)
  55. RANGED_COOLDOWN = 1200


  56. # ==============================================================================
  57. # 工具函数
  58. # ==============================================================================

  59. def distance(p1, p2):
  60.     """
  61.     计算两点之间的欧几里得距离。
  62.     参数:
  63.         p1 : (x, y) 元组,第一个点
  64.         p2 : (x, y) 元组,第二个点
  65.     返回:
  66.         float : 两点间的距离
  67.     """
  68.     # 用勾股定理计算距离:sqrt((x2-x1)^2 + (y2-y1)^2)
  69.     return math.hypot(p2[0] - p1[0], p2[1] - p1[1])


  70. def clamp(value, min_val, max_val):
  71.     """
  72.     将数值限制在 [min_val, max_val] 区间内。
  73.     参数:
  74.         value   : 要限制的数值
  75.         min_val : 下限
  76.         max_val : 上限
  77.     返回:
  78.         限制后的数值
  79.     """
  80.     # 如果 value 小于下限,返回下限;大于上限,返回上限;否则返回原值
  81.     if value < min_val:
  82.         return min_val
  83.     if value > max_val:
  84.         return max_val
  85.     return value


  86. # ==============================================================================
  87. # 子弹类(所有子弹统一用此类,通过 color / is_square 区分外观)
  88. # ==============================================================================

  89. class Bullet:
  90.     """
  91.     子弹类:代表从塔/远程小兵/远程怪物/主角色射出的飞行道具。
  92.     所有阵营的子弹共用此类,通过参数区分外观和伤害归属。
  93.     """

  94.     def __init__(self, x, y, target_x, target_y, speed, damage, color, is_square=False, size=6):
  95.         """
  96.         初始化一颗子弹。
  97.         参数:
  98.             x, y       : 子弹出生位置(像素坐标)
  99.             target_x, y : 子弹飞向的目标位置(像素坐标)
  100.             speed       : 飞行速度(像素/秒)
  101.             damage      : 命中时造成的伤害值
  102.             color       : 子弹颜色 (R,G,B)
  103.             is_square   : True=正方形外观(塔/主角色子弹),False=圆形外观
  104.             size        : 子弹边长/直径(像素)
  105.         """
  106.         # 记录出生坐标
  107.         self.x = x
  108.         self.y = y
  109.         # 计算子弹到目标的单位方向向量
  110.         dx = target_x - x
  111.         dy = target_y - y
  112.         dist = math.hypot(dx, dy)
  113.         # 避免除零(目标与出生点重合时)
  114.         if dist == 0:
  115.             self.vx = 0
  116.             self.vy = 0
  117.         else:
  118.             # 单位向量乘以速度得到每帧位移分量
  119.             self.vx = dx / dist * speed
  120.             self.vy = dy / dist * speed
  121.         # 飞行速度(像素/秒)
  122.         self.speed = speed
  123.         # 伤害值
  124.         self.damage = damage
  125.         # 子弹颜色
  126.         self.color = color
  127.         # 是否为正方形外观
  128.         self.is_square = is_square
  129.         # 子弹尺寸(边长或直径)
  130.         self.size = size
  131.         # 存活标记,False 时会被从列表中移除
  132.         self.alive = True

  133.     def update(self, dt):
  134.         """
  135.         更新子弹位置。
  136.         参数:
  137.             dt : 上一帧到这一帧的时间间隔(秒)
  138.         """
  139.         # 根据速度和时间增量移动子弹
  140.         self.x += self.vx * dt
  141.         self.y += self.vy * dt
  142.         # 如果子弹飞出屏幕范围,标记为死亡
  143.         if (self.x < -20 or self.x > SCREEN_WIDTH + 20 or
  144.                 self.y < -20 or self.y > SCREEN_HEIGHT + 20):
  145.             self.alive = False

  146.     def draw(self, screen):
  147.         """
  148.         在屏幕上绘制子弹。
  149.         参数:
  150.             screen : pygame 的 Surface 对象(游戏窗口)
  151.         """
  152.         if self.is_square:
  153.             # 正方形子弹:用 pygame.draw.rect 绘制
  154.             rect = pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  155.                                self.size, self.size)
  156.             pygame.draw.rect(screen, self.color, rect)
  157.         else:
  158.             # 圆形子弹:用 pygame.draw.circle 绘制
  159.             pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)),
  160.                                self.size // 2)

  161.     def get_rect(self):
  162.         """
  163.         获取子弹的碰撞矩形(用于检测是否击中目标)。
  164.         返回:
  165.             pygame.Rect 对象
  166.         """
  167.         # 以子弹中心为基准,生成一个边长=size 的正方形碰撞框
  168.         return pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  169.                            self.size, self.size)


  170. # ==============================================================================
  171. # 小兵基类(近战小兵 & 远程小兵的公共属性和行为)
  172. # ==============================================================================

  173. class Soldier:
  174.     """
  175.     小兵基类(我方单位)。
  176.     近战小兵 = 白色正方形;远程小兵 = 白色圆形。
  177.     小兵会围绕主角色(或玩家放置的红色三角标)行动,并自动攻击范围内的怪物。
  178.     """

  179.     def __init__(self, x, y, soldier_type):
  180.         """
  181.         初始化一个小兵。
  182.         参数:
  183.             x, y         : 出生位置
  184.             soldier_type : 'melee' 近战(正方形) / 'ranged' 远程(圆形)
  185.         """
  186.         # 出生坐标
  187.         self.x = x
  188.         self.y = y
  189.         # 小兵类型
  190.         self.soldier_type = soldier_type
  191.         # 近战用正方形,远程用圆形
  192.         if soldier_type == 'melee':
  193.             self.is_square = True   # 外观为正方形
  194.             self.size = 20          # 边长
  195.             self.max_hp = 60        # 最大生命值
  196.             self.speed = 90         # 移动速度(像素/秒)
  197.             self.attack_range = 28  # 近战攻击距离
  198.             self.attack_damage = 12 # 每次攻击伤害
  199.             self.attack_cooldown = 800  # 攻击冷却(毫秒)
  200.         else:
  201.             self.is_square = False  # 外观为圆形
  202.             self.size = 18          # 直径
  203.             self.max_hp = 40
  204.             self.speed = 75
  205.             self.attack_range = 160  # 远程攻击距离
  206.             self.attack_damage = 8
  207.             self.attack_cooldown = RANGED_COOLDOWN
  208.         # 当前生命值
  209.         self.hp = self.max_hp
  210.         # 是否存活
  211.         self.alive = True
  212.         # 攻击冷却计时器(毫秒),大于 0 时不能攻击
  213.         self.cooldown_timer = 0
  214.         # 当前锁定的攻击目标(怪物对象),None 表示无目标
  215.         self.target = None
  216.         # 围绕目标(主角色/三角标)的当前角度(弧度),用于圆周运动
  217.         self.orbit_angle = random.uniform(0, 2 * math.pi)
  218.         # 围绕半径(随机一个值,让小兵分散开)
  219.         self.orbit_radius = random.uniform(SOLDIER_ORBIT_MIN, SOLDIER_ORBIT_MAX)

  220.     def update(self, dt, monsters, marker_pos, hero_pos, bullets_soldier):
  221.         """
  222.         更新小兵状态:移动 + 索敌 + 攻击。
  223.         参数:
  224.             dt             : 时间增量(秒)
  225.             monsters       : 怪物列表(用于索敌)
  226.             marker_pos     : 三角标位置 (x,y) 或 None
  227.             hero_pos       : 主角色位置 (x,y)
  228.             bullets_soldier: 我方子弹列表(远程小兵射击时往里添加)
  229.         """
  230.         # 冷却计时器随时间递减
  231.         if self.cooldown_timer > 0:
  232.             self.cooldown_timer -= dt * 1000

  233.         # 确定围绕中心:优先三角标,其次主角色
  234.         if marker_pos is not None:
  235.             cx, cy = marker_pos
  236.         else:
  237.             cx, cy = hero_pos

  238.         # ---- 索敌:找射程内最近的怪物 ----
  239.         closest = None
  240.         closest_dist = self.attack_range
  241.         for m in monsters:
  242.             if not m.alive:
  243.                 continue
  244.             d = distance((self.x, self.y), (m.x, m.y))
  245.             if d < closest_dist:
  246.                 closest_dist = d
  247.                 closest = m
  248.         self.target = closest

  249.         if self.target is not None:
  250.             # 有目标且在射程内:停下进行攻击
  251.             if self.soldier_type == 'ranged':
  252.                 # 远程:发射子弹
  253.                 if self.cooldown_timer <= 0:
  254.                     b = Bullet(self.x, self.y, self.target.x, self.target.y,
  255.                                280, self.attack_damage, COLOR_WHITE,
  256.                                is_square=False, size=6)
  257.                     bullets_soldier.append(b)
  258.                     self.cooldown_timer = self.attack_cooldown
  259.             else:
  260.                 # 近战:直接扣血
  261.                 if self.cooldown_timer <= 0:
  262.                     self.target.take_damage(self.attack_damage)
  263.                     self.cooldown_timer = self.attack_cooldown
  264.         else:
  265.             # 无目标:围绕中心做圆周运动
  266.             self.orbit_angle += dt * 0.6  # 角速度
  267.             # 计算目标位置(圆周上的点)
  268.             tx = cx + math.cos(self.orbit_angle) * self.orbit_radius
  269.             ty = cy + math.sin(self.orbit_angle) * self.orbit_radius
  270.             # 平滑移向目标点
  271.             dx = tx - self.x
  272.             dy = ty - self.y
  273.             dist = math.hypot(dx, dy)
  274.             if dist > 2:
  275.                 self.x += dx / dist * self.speed * dt
  276.                 self.y += dy / dist * self.speed * dt
  277.             # 限制在屏幕内
  278.             self.x = clamp(self.x, 10, SCREEN_WIDTH - 10)
  279.             self.y = clamp(self.y, 10, SCREEN_HEIGHT - 10)

  280.     def take_damage(self, dmg):
  281.         """
  282.         受到伤害。
  283.         参数:
  284.             dmg : 伤害值
  285.         """
  286.         self.hp -= dmg
  287.         if self.hp <= 0:
  288.             self.alive = False

  289.     def draw(self, screen):
  290.         """
  291.         绘制小兵。
  292.         参数:
  293.             screen : 游戏窗口 Surface
  294.         """
  295.         if self.is_square:
  296.             # 近战小兵:白色正方形
  297.             rect = pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  298.                                self.size, self.size)
  299.             pygame.draw.rect(screen, COLOR_WHITE, rect)
  300.             # 加一个浅灰边框便于辨识
  301.             pygame.draw.rect(screen, (200, 200, 200), rect, 1)
  302.         else:
  303.             # 远程小兵:白色圆形
  304.             pygame.draw.circle(screen, COLOR_WHITE, (int(self.x), int(self.y)),
  305.                                self.size // 2)
  306.             pygame.draw.circle(screen, (200, 200, 200),
  307.                                (int(self.x), int(self.y)), self.size // 2, 1)
  308.         # 绘制血条(在头顶)
  309.         bar_w = 22
  310.         bar_h = 3
  311.         bx = self.x - bar_w / 2
  312.         by = self.y - self.size / 2 - 8
  313.         pygame.draw.rect(screen, (60, 60, 60), (bx, by, bar_w, bar_h))
  314.         hp_ratio = max(0, self.hp / self.max_hp)
  315.         pygame.draw.rect(screen, COLOR_GREEN, (bx, by, bar_w * hp_ratio, bar_h))


  316. # ==============================================================================
  317. # 怪物类(近战怪物 & 远程怪物)
  318. # ==============================================================================

  319. class Monster:
  320.     """
  321.     怪物类(敌方单位)。
  322.     近战怪物 = 黑色正方形;远程怪物 = 黑色圆形。
  323.     怪物沿一条固定的、拐几个弯的路径从屏幕顶部向底部(或左向右)行进。
  324.     """

  325.     def __init__(self, x, y, monster_type, path_points):
  326.         """
  327.         初始化一个怪物。
  328.         参数:
  329.             x, y         : 出生位置
  330.             monster_type : 'melee' 近战(正方形) / 'ranged' 远程(圆形)
  331.             path_points  : 路径点列表 [(x1,y1),(x2,y2),...],怪物依次走过
  332.         """
  333.         self.x = x
  334.         self.y = y
  335.         self.monster_type = monster_type
  336.         # 路径点副本
  337.         self.path = list(path_points)
  338.         # 当前目标路径点索引
  339.         self.path_index = 0
  340.         if monster_type == 'melee':
  341.             self.is_square = True
  342.             self.size = 22
  343.             self.max_hp = 50
  344.             self.speed = 60
  345.             self.attack_range = 30
  346.             self.attack_damage = 10
  347.             self.attack_cooldown = 900
  348.         else:
  349.             self.is_square = False
  350.             self.size = 20
  351.             self.max_hp = 35
  352.             self.speed = 50
  353.             self.attack_range = 150
  354.             self.attack_damage = 7
  355.             self.attack_cooldown = RANGED_COOLDOWN
  356.         self.hp = self.max_hp
  357.         self.alive = True
  358.         self.cooldown_timer = 0
  359.         self.target = None  # 锁定的小兵目标

  360.     def update(self, dt, soldiers, bullets_monster):
  361.         """
  362.         更新怪物:沿路径移动 + 索敌 + 攻击。
  363.         参数:
  364.             dt              : 时间增量(秒)
  365.             soldiers        : 我方小兵列表(用于索敌)
  366.             bullets_monster : 敌方子弹列表(远程怪物射击时添加)
  367.         """
  368.         if self.cooldown_timer > 0:
  369.             self.cooldown_timer -= dt * 1000

  370.         # ---- 索敌:找射程内最近的小兵 ----
  371.         closest = None
  372.         closest_dist = self.attack_range
  373.         for s in soldiers:
  374.             if not s.alive:
  375.                 continue
  376.             d = distance((self.x, self.y), (s.x, s.y))
  377.             if d < closest_dist:
  378.                 closest_dist = d
  379.                 closest = s
  380.         self.target = closest

  381.         if self.target is not None:
  382.             # 有目标:停下攻击
  383.             if self.monster_type == 'ranged':
  384.                 if self.cooldown_timer <= 0:
  385.                     b = Bullet(self.x, self.y, self.target.x, self.target.y,
  386.                                240, self.attack_damage, COLOR_BLACK,
  387.                                is_square=False, size=6)
  388.                     bullets_monster.append(b)
  389.                     self.cooldown_timer = self.attack_cooldown
  390.             else:
  391.                 if self.cooldown_timer <= 0:
  392.                     self.target.take_damage(self.attack_damage)
  393.                     self.cooldown_timer = self.attack_cooldown
  394.         else:
  395.             # 无目标:沿路径前进
  396.             if self.path_index < len(self.path):
  397.                 tx, ty = self.path[self.path_index]
  398.                 dx = tx - self.x
  399.                 dy = ty - self.y
  400.                 dist = math.hypot(dx, dy)
  401.                 if dist < 5:
  402.                     # 到达当前路径点,前往下一个
  403.                     self.path_index += 1
  404.                 else:
  405.                     self.x += dx / dist * self.speed * dt
  406.                     self.y += dy / dist * self.speed * dt

  407.     def take_damage(self, dmg):
  408.         """受到伤害。"""
  409.         self.hp -= dmg
  410.         if self.hp <= 0:
  411.             self.alive = False

  412.     def draw(self, screen):
  413.         """绘制怪物。"""
  414.         if self.is_square:
  415.             # 近战怪物:黑色正方形
  416.             rect = pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  417.                                self.size, self.size)
  418.             pygame.draw.rect(screen, COLOR_BLACK, rect)
  419.             pygame.draw.rect(screen, (50, 50, 50), rect, 1)
  420.         else:
  421.             # 远程怪物:黑色圆形
  422.             pygame.draw.circle(screen, COLOR_BLACK, (int(self.x), int(self.y)),
  423.                                self.size // 2)
  424.             pygame.draw.circle(screen, (50, 50, 50),
  425.                                (int(self.x), int(self.y)), self.size // 2, 1)
  426.         # 血条
  427.         bar_w = 24
  428.         bar_h = 3
  429.         bx = self.x - bar_w / 2
  430.         by = self.y - self.size / 2 - 8
  431.         pygame.draw.rect(screen, (60, 60, 60), (bx, by, bar_w, bar_h))
  432.         hp_ratio = max(0, self.hp / self.max_hp)
  433.         pygame.draw.rect(screen, (220, 60, 60), (bx, by, bar_w * hp_ratio, bar_h))

  434.     def reached_end(self):
  435.         """
  436.         判断怪物是否已走完全部路径点(到达终点)。
  437.         返回:
  438.             True/False
  439.         """
  440.         return self.path_index >= len(self.path)


  441. # ==============================================================================
  442. # 塔类(玩家按 T 键在主角色位置建造)
  443. # ==============================================================================

  444. class Tower:
  445.     """
  446.     塔类:玩家按 T 键在主角色当前位置建造。
  447.     外观:红色正方形。自动攻击射程内最近的怪物,发射红色小正方形子弹。
  448.     """

  449.     def __init__(self, x, y):
  450.         """初始化一座塔。"""
  451.         self.x = x
  452.         self.y = y
  453.         self.size = 26                # 塔的边长
  454.         self.range = TOWER_RANGE      # 攻击范围
  455.         self.damage = 18              # 每次命中伤害
  456.         self.cooldown_max = TOWER_COOLDOWN  # 射击冷却(毫秒)
  457.         self.cooldown_timer = 0       # 当前冷却计时
  458.         self.alive = True             # 塔始终存活(本验证版不可被攻击)

  459.     def update(self, dt, monsters, bullets_tower):
  460.         """
  461.         塔的逻辑更新:索敌 + 射击。
  462.         参数:
  463.             dt           : 时间增量(秒)
  464.             monsters     : 怪物列表
  465.             bullets_tower: 塔子弹列表(射击时添加)
  466.         """
  467.         if self.cooldown_timer > 0:
  468.             self.cooldown_timer -= dt * 1000
  469.         # 找射程内最近的怪物
  470.         closest = None
  471.         closest_dist = self.range
  472.         for m in monsters:
  473.             if not m.alive:
  474.                 continue
  475.             d = distance((self.x, self.y), (m.x, m.y))
  476.             if d < closest_dist:
  477.                 closest_dist = d
  478.                 closest = m
  479.         if closest is not None and self.cooldown_timer <= 0:
  480.             # 发射红色小正方形子弹
  481.             b = Bullet(self.x, self.y, closest.x, closest.y,
  482.                        320, self.damage, COLOR_RED, is_square=True, size=8)
  483.             bullets_tower.append(b)
  484.             self.cooldown_timer = self.cooldown_max

  485.     def draw(self, screen):
  486.         """绘制塔(红色正方形 + 攻击范围圈)。"""
  487.         # 先画半透明的攻击范围圈(淡红色)
  488.         surf = pygame.Surface((self.range * 2, self.range * 2), pygame.SRCALPHA)
  489.         pygame.draw.circle(surf, (220, 30, 30, 25),
  490.                            (self.range, self.range), self.range)
  491.         screen.blit(surf, (self.x - self.range, self.y - self.range))
  492.         # 画塔主体(红色正方形)
  493.         rect = pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  494.                            self.size, self.size)
  495.         pygame.draw.rect(screen, COLOR_RED, rect)
  496.         pygame.draw.rect(screen, (255, 120, 120), rect, 2)


  497. # ==============================================================================
  498. # 主角色类(玩家操控的红色正方形)
  499. # ==============================================================================

  500. class Hero:
  501.     """
  502.     主角色类:玩家用方向键/WASD 操控的红色正方形。
  503.     小兵默认围绕主角色行动;玩家可在地图上放置红色三角标,
  504.     放置后小兵改为围绕三角标行动。
  505.     """

  506.     def __init__(self, x, y):
  507.         """初始化主角色。"""
  508.         self.x = x
  509.         self.y = y
  510.         self.size = 24          # 边长
  511.         self.speed = HERO_SPEED  # 移动速度
  512.         self.hp = 100           # 生命值
  513.         self.max_hp = 100
  514.         self.alive = True

  515.     def update(self, dt, keys):
  516.         """
  517.         根据按键更新主角色位置。
  518.         参数:
  519.             dt    : 时间增量(秒)
  520.             keys  : pygame 按键状态字典
  521.         """
  522.         dx = 0
  523.         dy = 0
  524.         # 检测方向键和 WASD
  525.         if keys[pygame.K_LEFT] or keys[pygame.K_a]:
  526.             dx -= 1
  527.         if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
  528.             dx += 1
  529.         if keys[pygame.K_UP] or keys[pygame.K_w]:
  530.             dy -= 1
  531.         if keys[pygame.K_DOWN] or keys[pygame.K_s]:
  532.             dy += 1
  533.         # 归一化对角线移动(防止斜向速度过快)
  534.         if dx != 0 and dy != 0:
  535.             dx *= 0.7071
  536.             dy *= 0.7071
  537.         self.x += dx * self.speed * dt
  538.         self.y += dy * self.speed * dt
  539.         # 限制在屏幕内
  540.         self.x = clamp(self.x, self.size / 2, SCREEN_WIDTH - self.size / 2)
  541.         self.y = clamp(self.y, self.size / 2, SCREEN_HEIGHT - self.size / 2)

  542.     def draw(self, screen):
  543.         """绘制主角色(红色正方形 + 血条)。"""
  544.         rect = pygame.Rect(self.x - self.size / 2, self.y - self.size / 2,
  545.                            self.size, self.size)
  546.         pygame.draw.rect(screen, COLOR_RED, rect)
  547.         pygame.draw.rect(screen, (255, 160, 160), rect, 2)
  548.         # 血条
  549.         bar_w = 30
  550.         bar_h = 4
  551.         bx = self.x - bar_w / 2
  552.         by = self.y - self.size / 2 - 10
  553.         pygame.draw.rect(screen, (60, 60, 60), (bx, by, bar_w, bar_h))
  554.         hp_ratio = max(0, self.hp / self.max_hp)
  555.         pygame.draw.rect(screen, COLOR_GREEN, (bx, by, bar_w * hp_ratio, bar_h))


  556. # ==============================================================================
  557. # 红色三角标类(玩家鼠标左键放置的集结点)
  558. # ==============================================================================

  559. class Marker:
  560.     """
  561.     红色三角标:玩家鼠标左键点击地图后生成。
  562.     小兵检测到三角标存在时,改为围绕三角标而非主角色行动。
  563.     """

  564.     def __init__(self, x, y):
  565.         """初始化三角标。"""
  566.         self.x = x
  567.         self.y = y
  568.         self.size = 16  # 三角标尺寸
  569.         self.alive = True
  570.         # 脉冲动画计时(让三角标有呼吸效果)
  571.         self.pulse = 0

  572.     def update(self, dt):
  573.         """更新脉冲动画计时。"""
  574.         self.pulse += dt * 4

  575.     def draw(self, screen):
  576.         """绘制红色三角标(向下指的三角形 + 外圈脉冲)。"""
  577.         # 脉冲外圈
  578.         pulse_r = self.size + math.sin(self.pulse) * 6 + 6
  579.         pygame.draw.circle(screen, (230, 210, 30, 60),
  580.                            (int(self.x), int(self.y)), int(pulse_r), 1)
  581.         # 三角形三个顶点(指向下方)
  582.         s = self.size
  583.         p1 = (self.x, self.y - s * 0.7)   # 上顶点
  584.         p2 = (self.x - s * 0.6, self.y + s * 0.5)  # 左下
  585.         p3 = (self.x + s * 0.6, self.y + s * 0.5)  # 右下
  586.         pygame.draw.polygon(screen, COLOR_YELLOW, [p1, p2, p3])
  587.         pygame.draw.polygon(screen, (180, 160, 20), [p1, p2, p3], 2)


  588. # ==============================================================================
  589. # 游戏主类(管理所有状态和主循环)
  590. # ==============================================================================

  591. class Game:
  592.     """游戏主类,负责初始化、主循环、事件处理、碰撞检测等。"""

  593.     def __init__(self):
  594.         """初始化 pygame 和游戏状态。"""
  595.         # 初始化 pygame 视频模块
  596.         pygame.init()
  597.         pygame.display.set_caption(u"塔防游戏")
  598.         # 创建游戏窗口
  599.         self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
  600.         # 创建时钟对象,用于限制帧率
  601.         self.clock = pygame.time.Clock()
  602.         # 创建字体对象(默认字体,支持中文需系统有中文字体)
  603.         # 使用 pygame.font.SysFont 并指定中文字体名称列表,避免中文乱码
  604.         self.font = pygame.font.SysFont(
  605.             "noto sans cjk sc,wenquanyi micro hei,microsoft yahei,simhei,arial",
  606.             20)
  607.         self.font_small = pygame.font.SysFont(
  608.             "noto sans cjk sc,wenquanyi micro hei,microsoft yahei,simhei,arial",
  609.             16)
  610.         self.font_big = pygame.font.SysFont(
  611.             "noto sans cjk sc,wenquanyi micro hei,microsoft yahei,simhei,arial",
  612.             48)

  613.         # ---- 游戏状态变量 ----
  614.         self.running = True          # 主循环是否继续
  615.         self.game_over = False       # 是否游戏结束
  616.         self.score = 0               # 当前得分
  617.         self.leaked = 0              # 漏掉的怪物数
  618.         self.max_leaked = 10         # 允许漏掉的最大值,超过则游戏结束
  619.         self.wave = 1                # 当前波次
  620.         self.wave_timer = 0          # 波次内计时(毫秒)
  621.         self.spawn_timer = 0         # 怪物生成计时(毫秒)
  622.         self.monsters_to_spawn = 0   # 当前波次剩余待生成怪物数

  623.         # ---- 游戏对象列表 ----
  624.         self.hero = Hero(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
  625.         self.marker = None            # 红色三角标(None=未放置)
  626.         self.soldiers = []           # 我方小兵列表
  627.         self.monsters = []           # 敌方怪物列表
  628.         self.towers = []             # 塔列表
  629.         self.bullets_tower = []      # 塔发射的子弹
  630.         self.bullets_soldier = []    # 小兵发射的子弹
  631.         self.bullets_monster = []    # 怪物发射的子弹

  632.         # ---- 固定路径定义(怪物走这条路径,拐几个弯)----
  633.         # 路径:从屏幕上方中间出发,先向右下,再向左,再向下出屏
  634.         self.monster_path = [
  635.             (SCREEN_WIDTH // 2, -20),       # 起点(屏外上方)
  636.             (SCREEN_WIDTH // 2, 120),       # 第一段:向下
  637.             (SCREEN_WIDTH - 120, 120),      # 第二段:向右
  638.             (SCREEN_WIDTH - 120, 380),      # 第三段:向下
  639.             (140, 380),                     # 第四段:向左
  640.             (140, SCREEN_HEIGHT - 60),      # 第五段:向下
  641.             (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 60),  # 第六段:向右
  642.             (SCREEN_WIDTH // 2, SCREEN_HEIGHT + 30),  # 终点(屏外下方)
  643.         ]

  644.         # 启动第一波
  645.         self._start_wave()

  646.     # --------------------------------------------------------------------------
  647.     # 内部方法
  648.     # --------------------------------------------------------------------------

  649.     def _start_wave(self):
  650.         """开始新的一波:计算本波要生成的怪物数量和间隔。"""
  651.         # 每波怪物数量 = 5 + 波次*2
  652.         self.monsters_to_spawn = 5 + self.wave * 2
  653.         # 生成间隔随波次递减(最快 600ms)
  654.         self.spawn_interval = max(600, 1800 - self.wave * 150)
  655.         self.spawn_timer = 0

  656.     def _spawn_monster(self):
  657.         """生成一只怪物(从路径起点出发)。"""
  658.         # 起点为路径第一个点
  659.         sx, sy = self.monster_path[0]
  660.         # 70% 近战 + 30% 远程,随波次提高远程比例
  661.         ranged_chance = min(0.4, 0.2 + self.wave * 0.03)
  662.         mtype = 'ranged' if random.random() < ranged_chance else 'melee'
  663.         # 怪物的生命值随波次提升
  664.         m = Monster(sx, sy, mtype, self.monster_path)
  665.         m.max_hp = int(m.max_hp * (1 + self.wave * 0.15))
  666.         m.hp = m.max_hp
  667.         self.monsters.append(m)

  668.     def _handle_events(self):
  669.         """处理 pygame 事件队列(键盘/鼠标/退出)。"""
  670.         # 遍历所有待处理事件
  671.         for event in pygame.event.get():
  672.             if event.type == pygame.QUIT:
  673.                 # 点击关闭按钮
  674.                 self.running = False
  675.             elif event.type == pygame.KEYDOWN:
  676.                 # 按键按下
  677.                 self._handle_keydown(event.key)
  678.             elif event.type == pygame.MOUSEBUTTONDOWN:
  679.                 # 鼠标按键
  680.                 self._handle_mouse(event)

  681.     def _handle_keydown(self, key):
  682.         """
  683.         处理按键事件。
  684.         参数:
  685.             key : 被按下的键(pygame 常量)
  686.         """
  687.         if self.game_over:
  688.             # 游戏结束后只响应 R 键重新开始
  689.             if key == pygame.K_r:
  690.                 self._restart()
  691.             elif key == pygame.K_ESCAPE:
  692.                 self.running = False
  693.             return

  694.         if key == pygame.K_1:
  695.             # 1 键:在主角色附近生成一支近战小兵
  696.             self._spawn_soldier('melee')
  697.         elif key == pygame.K_2:
  698.             # 2 键:在主角色附近生成一支远程小兵
  699.             self._spawn_soldier('ranged')
  700.         elif key == pygame.K_t:
  701.             # T 键:在主角色当前位置建造一座塔
  702.             print(0)
  703.             self._build_tower()
  704.         elif key == pygame.K_ESCAPE:
  705.             self.running = False

  706.     def _handle_mouse(self, event):
  707.         """
  708.         处理鼠标事件。
  709.         参数:
  710.             event : pygame 鼠标事件对象
  711.         """
  712.         if self.game_over:
  713.             return
  714.         if event.button == 1:
  715.             # 鼠标左键:在点击位置放置/移动红色三角标
  716.             mx, my = event.pos
  717.             self.marker = Marker(mx, my)
  718.         elif event.button == 3:
  719.             # 鼠标右键:取消三角标
  720.             self.marker = None

  721.     def _spawn_soldier(self, stype):
  722.         """
  723.         在主角色附近随机偏移位置生成一支小兵。
  724.         参数:
  725.             stype : 'melee' 或 'ranged'
  726.         """
  727.         # 在主角位置附近随机偏移,避免重叠
  728.         ox = random.uniform(-30, 30)
  729.         oy = random.uniform(-30, 30)
  730.         s = Soldier(self.hero.x + ox, self.hero.y + oy, stype)
  731.         self.soldiers.append(s)

  732.     def _build_tower(self):
  733.         """在主角色当前位置建造一座塔(最多允许 8 座)。"""
  734.         if len(self.towers) >= 8:
  735.             return  # 达到上限
  736.         # 检查该位置是否已有塔(避免重叠)
  737.         print(1)
  738.         for t in self.towers:
  739.             if distance((t.x, t.y), (self.hero.x, self.hero.y)) < 30:
  740.                 return
  741.         print(2)
  742.         self.towers.append(Tower(self.hero.x, self.hero.y))

  743.     def _update(self, dt):
  744.         """
  745.         更新所有游戏对象(每帧调用一次)。
  746.         参数:
  747.             dt : 时间增量(秒)
  748.         """
  749.         if self.game_over:
  750.             return

  751.         # 获取当前按键状态
  752.         keys = pygame.key.get_pressed()

  753.         # 更新主角色
  754.         self.hero.update(dt, keys)
  755.         # 更新三角标(脉冲动画)
  756.         if self.marker:
  757.             self.marker.update(dt)

  758.         # ---- 更新塔 ----
  759.         for t in self.towers:
  760.             t.update(dt, self.monsters, self.bullets_tower)

  761.         # ---- 更新小兵 ----
  762.         marker_pos = (self.marker.x, self.marker.y) if self.marker else None
  763.         hero_pos = (self.hero.x, self.hero.y)
  764.         for s in self.soldiers:
  765.             s.update(dt, self.monsters, marker_pos, hero_pos,
  766.                      self.bullets_soldier)

  767.         # ---- 更新怪物(生成 + 行为)----
  768.         self._update_monster_spawning(dt)
  769.         for m in self.monsters:
  770.             m.update(dt, self.soldiers, self.bullets_monster)
  771.             # 检测是否走完全程(漏怪)
  772.             if m.reached_end() and m.alive:
  773.                 m.alive = False
  774.                 self.leaked += 1
  775.                 self.score = max(0, self.score - 5)  # 漏怪扣分
  776.                 if self.leaked >= self.max_leaked:
  777.                     self.game_over = True

  778.         # ---- 更新所有子弹 ----
  779.         self._update_bullets(dt)

  780.         # ---- 清理死亡对象 ----
  781.         self._cleanup_dead()

  782.         # ---- 波次推进 ----
  783.         if self.monsters_to_spawn == 0 and len(self.monsters) == 0:
  784.             # 本波清空,进入下一波
  785.             self.wave += 1
  786.             self.score += 50 * self.wave  # 过关奖励
  787.             self._start_wave()

  788.         # 得分随时间小幅增长
  789.         self.score += dt * 2

  790.     def _update_monster_spawning(self, dt):
  791.         """管理怪物按间隔逐只生成。"""
  792.         if self.monsters_to_spawn > 0:
  793.             self.spawn_timer += dt * 1000
  794.             if self.spawn_timer >= self.spawn_interval:
  795.                 self._spawn_monster()
  796.                 self.monsters_to_spawn -= 1
  797.                 self.spawn_timer = 0

  798.     def _update_bullets(self, dt):
  799.         """更新子弹位置并检测碰撞。"""
  800.         # --- 塔子弹(打怪物)---
  801.         for b in self.bullets_tower:
  802.             b.update(dt)
  803.             if not b.alive:
  804.                 continue
  805.             # 检测是否击中某只怪物
  806.             for m in self.monsters:
  807.                 if not m.alive:
  808.                     continue
  809.                 if b.get_rect().colliderect(m.x - m.size/2, m.y - m.size/2,
  810.                                             m.size, m.size):
  811.                     m.take_damage(b.damage)
  812.                     b.alive = False
  813.                     if not m.alive:
  814.                         self.score += 10  # 击杀得分
  815.                     break

  816.         # --- 小兵子弹(打怪物)---
  817.         for b in self.bullets_soldier:
  818.             b.update(dt)
  819.             if not b.alive:
  820.                 continue
  821.             for m in self.monsters:
  822.                 if not m.alive:
  823.                     continue
  824.                 if b.get_rect().colliderect(m.x - m.size/2, m.y - m.size/2,
  825.                                             m.size, m.size):
  826.                     m.take_damage(b.damage)
  827.                     b.alive = False
  828.                     if not m.alive:
  829.                         self.score += 5
  830.                     break

  831.         # --- 怪物子弹(打小兵)---
  832.         for b in self.bullets_monster:
  833.             b.update(dt)
  834.             if not b.alive:
  835.                 continue
  836.             for s in self.soldiers:
  837.                 if not s.alive:
  838.                     continue
  839.                 if b.get_rect().colliderect(s.x - s.size/2, s.y - s.size/2,
  840.                                             s.size, s.size):
  841.                     s.take_damage(b.damage)
  842.                     b.alive = False
  843.                     break

  844.     def _cleanup_dead(self):
  845.         """移除所有标记为死亡的子弹/小兵/怪物,释放内存。"""
  846.         self.bullets_tower = [b for b in self.bullets_tower if b.alive]
  847.         self.bullets_soldier = [b for b in self.bullets_soldier if b.alive]
  848.         self.bullets_monster = [b for b in self.bullets_monster if b.alive]
  849.         self.soldiers = [s for s in self.soldiers if s.alive]
  850.         self.monsters = [m for m in self.monsters if m.alive]

  851.     def _draw(self):
  852.         """绘制当前帧的所有内容到屏幕。"""
  853.         # 填充背景色
  854.         self.screen.fill(COLOR_BG)

  855.         # 绘制路径辅助线(淡灰色虚线,便于观察怪物行进路线)
  856.         self._draw_path()

  857.         # 绘制三角标
  858.         if self.marker:
  859.             self.marker.draw(self.screen)

  860.         # 绘制塔
  861.         for t in self.towers:
  862.             t.draw(self.screen)
  863.         # 绘制小兵
  864.         for s in self.soldiers:
  865.             s.draw(self.screen)
  866.         # 绘制怪物
  867.         for m in self.monsters:
  868.             m.draw(self.screen)
  869.         # 绘制主角色(最后绘制,显示在最上层)
  870.         self.hero.draw(self.screen)

  871.         # 绘制所有子弹
  872.         for b in self.bullets_tower:
  873.             b.draw(self.screen)
  874.         for b in self.bullets_soldier:
  875.             b.draw(self.screen)
  876.         for b in self.bullets_monster:
  877.             b.draw(self.screen)

  878.         # 绘制 UI(得分/波次/漏怪数/操作提示)
  879.         self._draw_ui()

  880.         # 如果游戏结束,绘制半透明遮罩 + 结束文字
  881.         if self.game_over:
  882.             self._draw_game_over()

  883.         # 将绘制内容刷新到屏幕
  884.         pygame.display.flip()

  885.     def _draw_path(self):
  886.         """绘制怪物路径的辅助线(浅灰虚线)。"""
  887.         pts = [(int(p[0]), int(p[1])) for p in self.monster_path]
  888.         if len(pts) < 2:
  889.             return
  890.         for i in range(len(pts) - 1):
  891.             # 用浅灰色画路径线段
  892.             pygame.draw.line(self.screen, (60, 60, 72), pts[i], pts[i+1], 2)
  893.         # 在路径拐点画小圆点标记
  894.         for p in pts:
  895.             pygame.draw.circle(self.screen, (80, 80, 95), p, 4)

  896.     def _draw_ui(self):
  897.         """绘制顶部状态栏和操作提示。"""
  898.         # ---- 顶部状态信息 ----
  899.         info_lines = [
  900.             u"得分: %d" % int(self.score),
  901.             u"波次: %d" % self.wave,
  902.             u"漏怪: %d/%d" % (self.leaked, self.max_leaked),
  903.             u"小兵: %d" % len(self.soldiers),
  904.             u"塔: %d/8" % len(self.towers),
  905.         ]
  906.         x0 = 10
  907.         y0 = 8
  908.         for i, line in enumerate(info_lines):
  909.             surf = self.font_small.render(line, True, (220, 220, 220))
  910.             self.screen.blit(surf, (x0, y0 + i * 20))

  911.         # ---- 右下角操作提示 ----
  912.         tips = [
  913.             u"[方向键/WASD] 移动主角",
  914.             u"[鼠标左键] 放置红色三角标",
  915.             u"[鼠标右键] 取消三角标",
  916.             u"[1] 生成近战小兵(白方块)",
  917.             u"[2] 生成远程小兵(白圆)",
  918.             u"[T] 在主角位置建塔",
  919.             u"[R] 重新开始  [ESC] 退出",
  920.         ]
  921.         for i, tip in enumerate(tips):
  922.             surf = self.font_small.render(tip, True, (170, 170, 170))
  923.             self.screen.blit(surf, (SCREEN_WIDTH - 280,
  924.                                     SCREEN_HEIGHT - 140 + i * 20))

  925.         # 三角标状态提示
  926.         if self.marker:
  927.             surf = self.font_small.render(u"小兵围绕三角标行动中",
  928.                                           True, COLOR_YELLOW)
  929.             self.screen.blit(surf, (SCREEN_WIDTH // 2 - 80, 10))

  930.     def _draw_game_over(self):
  931.         """绘制游戏结束画面(半透明遮罩 + 文字)。"""
  932.         # 半透明黑色遮罩
  933.         overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT),
  934.                                  pygame.SRCALPHA)
  935.         overlay.fill((0, 0, 0, 160))
  936.         self.screen.blit(overlay, (0, 0))
  937.         # 游戏结束大字
  938.         text = self.font_big.render(u"游戏结束", True, (230, 60, 60))
  939.         rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 40))
  940.         self.screen.blit(text, rect)
  941.         # 得分信息
  942.         sub = self.font.render(u"最终得分: %d   到达波次: %d" %
  943.                                (int(self.score), self.wave), True,
  944.                                (220, 220, 220))
  945.         rect2 = sub.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 10))
  946.         self.screen.blit(sub, rect2)
  947.         # 提示按键
  948.         tip = self.font.render(u"按 [R] 重新开始   按 [ESC] 退出",
  949.                                True, (180, 180, 180))
  950.         rect3 = tip.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50))
  951.         self.screen.blit(tip, rect3)

  952.     def _restart(self):
  953.         print("restart!!")
  954.         """重置所有游戏状态,开始新一局。"""
  955.         self.__init__()

  956.     # --------------------------------------------------------------------------
  957.     # 主循环
  958.     # --------------------------------------------------------------------------

  959.     def run(self):
  960.         """游戏主循环:持续处理事件、更新状态、绘制画面。"""
  961.         while self.running:
  962.             # 计算上一帧到这一帧的时间间隔(秒),用于帧率无关运动
  963.             dt = self.clock.tick(FPS) / 1000.0
  964.             # 限制 dt 上限,防止切到后台后回来出现巨大跳跃
  965.             if dt > 0.1:
  966.                 dt = 0.1
  967.             # 处理事件
  968.             self._handle_events()
  969.             # 更新游戏状态
  970.             self._update(dt)
  971.             # 绘制当前帧
  972.             self._draw()
  973.         # 退出 pygame
  974.         pygame.quit()
  975.         sys.exit()


  976. # ==============================================================================
  977. # 程序入口
  978. # ==============================================================================

  979. if __name__ == "__main__":
  980.     # 创建游戏实例并启动主循环
  981.     game = Game()
  982.     game.run()
复制代码

评分

参与人数 2荣誉 +7 鱼币 +7 贡献 +5 收起 理由
cjjJasonchen + 5 + 5 + 3 劲呐~
小甲鱼的二师兄 + 2 + 2 + 2 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2026-8-28 08:15:58 | 显示全部楼层
你好鱼油! 你贴出了一个塔防游戏的代码,看起来是用 pygame 写的一个简单版本。我帮你梳理一下,并指出几个需要注意的地方。

1. 代码结构
代码分为几个核心部分:
  • 全局常量(屏幕大小、颜色、速度等)
  • 工具函数(距离计算、数值限制)
  • 子弹类(Bullet)
  • 小兵类(Soldier,目前只看到近战和远程小兵的定义)


操作上支持移动、放置三角标、建塔、生成小兵、重新开始和退出,功能还挺完整的。

2. 发现一个明显的 Bug
distance 函数里:

  1. def distance(p1, p2):
  2.     return math.hypot(p2 - p1, p2 - p1)
复制代码

这里的 p1p2 应该是坐标元组(如 (x1, y1)),不能直接相减。应该写成:

  1. def distance(p1, p2):
  2.     dx = p2[0] - p1[0]
  3.     dy = p2[1] - p1[1]
  4.     return math.hypot(dx, dy)
复制代码

否则运行时会报错,或者算出来的距离是错的。

3. 代码不完整
你目前只贴到了 Soldier.update 的一部分,后续应该还有怪物类、塔类、主角色类、以及主循环等。如果直接运行会报“缩进错误”或“意外结束”。需要把完整代码发出来,或者补全后再测试。

4. 运行环境
确保已经安装了 pygame:
  1. pip install pygame
复制代码


5. 小建议
建议先修正 distance 函数,再把代码补完整,如果遇到问题随时发到论坛,我们一起排查。

加油,期待你的完整作品!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2026-8-28 08:29:24 | 显示全部楼层
支持哇!!!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2026-8-28 09:42:29 | 显示全部楼层
小时候玩的王国保卫战,猴子气球,还有一些造建筑产兵对战的游戏,都很好玩很有意思,当时在4399玩得不亦乐乎
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2026-8-29 15:15:06 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2026-8-30 10:48:11 | 显示全部楼层
大制作呢
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2026-8-30 18:51:42 | 显示全部楼层

大佬我就一准大一,趁着高考暑假写一下,而且上次写代码还是小学买小甲鱼的书,捡起来磕磕绊绊的,实在是功力不足,其实后面还有个尝试加开始界面的,结果疯狂报错,我现在还在尝试调试
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2026-8-31 08:23:04 | 显示全部楼层
磊之茶 发表于 2026-8-30 18:51
大佬我就一准大一,趁着高考暑假写一下,而且上次写代码还是小学买小甲鱼的书,捡起来磕磕绊绊的,实在是 ...

我也是小萌新捏,比你大一岁哈哈哈
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-9-18 21:20

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表