Python实现海洋鱼群动态系统绘制:详细步骤与完整代码分享

一款有趣的初级海洋生态动画模拟系统(初级版,点赞破300,继续更新强化版)

先看Gif动图效果:

视频效果:

python绘制初级海洋鱼群系统!

本系统基于Python构建了一个可视化海洋生态系统,生动呈现了鲨鱼、小鱼群和用户干预的动态平衡关系。通过Pygame引擎实现实时动画渲染,采用面向对象编程构建生物智能体模型,用户可通过交互式按钮探索生态系统的非线性特征。

界面目前具有三大功能!!!,通过按钮实现添加鱼、添加鲨鱼、重置场景的三大操作!!

笔者文章点赞破300,作者立刻更新强化版新功能;

具体界面展示:

 代码讲解:

我将用「海洋探险」的视角,把代码拆解成一个个「探险章节」,用生动的比喻带小白轻松理解这个海洋生态模拟程序的核心逻辑,过程中会避开复杂术语,重点讲清「代码在做什么」和「为什么这样做」:

看到这里;觉得有趣的朋友可以点赞+收藏,点赞破300!,笔者后续会更新强化版本;

🌊 第一章:搭建海洋舞台(导入库与初始化)

python

运行

import tkinter as tk
import random
import math
from tkinter import ttk
  • 舞台搭建工具
    tkinter 是 Python 自带的「GUI 积木」,用来搭建窗口和画布;
    random 像「海洋魔术师」,负责随机生成鱼的位置、颜色等;
    math 是「物理引擎」,计算鱼的游动弧度、鲨鱼的转向角度等。
  • python

    运行

    class MarineEcosystem:
        def __init__(self, root):
            self.root = root
            self.root.title("海洋生态系统")
            self.root.geometry("1024x768")
            # 屏幕适配代码...
    
  • 主角登场
    MarineEcosystem 类是「生态系统管理员」,负责创建整个海洋世界。
    __init__ 方法像「开幕仪式」,设置窗口标题、大小,并让窗口适应不同屏幕。
  • 🌊 第二章:绘制深海背景(渐变海与动态水波)

    python

    运行

    def create_background(self):
        for i in range(0, 100):
            gradient = 35 + int(20 * math.sin(i * 0.1))
            self.canvas.create_rectangle(..., fill=f"#0006{gradient:02x}")
    

  • 渐变深海魔法
    用循环画 100 个横向长条,每个长条的蓝色深度用 sin 函数随机波动(类似阳光穿透不同深度海水的效果),最终形成「会呼吸的深蓝色海洋」。
  • python

    运行

    def create_waves(self):
        wave_colors = ["#1a4580", "#1a2f80", "#1a1f80"]
        for i in range(3):
            self.waves.append({...})
    

  • 三层水波特效
    创建 3 层不同深浅的蓝色波浪,每层波浪有独立的「速度」和「波动相位」,通过 update_waves 方法让它们像真实海浪一样「错位晃动」,营造立体感。
  • 🌊 第三章:海洋居民诞生(鱼与鲨鱼的诞生)

    python

    运行

    def create_fish(self, count):
        for _ in range(count):
            fish = {
                "x": random.uniform(100, 924),  # 随机出生位置
                "size": random.uniform(0.6, 1.8),  # 体型随机
                "color": self.generate_fish_color(),  # 随机颜色
                "direction": random.choice([-1, 1]),  # 初始游动方向
                # 其他参数...
            }
            self.fish.append(fish)
    

  • 小鱼的「出生参数」
    每条鱼有自己的「体型、颜色、速度、波动幅度」,甚至有「恐惧等级」和「饥饿值」,像真实生物一样有生存状态。
    generate_fish_color 方法会从蓝、绿、橙等色系中随机搭配颜色,让每条鱼都独一无二。
  • python

    运行

    def create_sharks(self, count):
        for _ in range(count):
            shark = {
                "y": random.uniform(50, self.screen_height*0.7),  # 鲨鱼偏爱中层水域
                "speed": random.uniform(3.0, 6.0),  # 比鱼游得快
                "color": self.generate_shark_color(),  # 灰蓝色皮肤
                # 其他参数...
            }
            self.sharks.append(shark)
    

  • 鲨鱼的「猎手特性」
    体型更大、速度更快,颜色模拟真实鲨鱼的「上深下浅」保护色,还有「饥饿值」和「捕猎状态」,会主动寻找猎物。
  • 🌊 第四章:生动的游动动画(绘制与更新)

    python

    运行

    def draw_fish(self, fish):
        # 计算鱼的动态位置和变形
        vert_offset = math.sin(math.radians(fish["phase"])) * fish["wave_amp"] * 10
        body_sway = math.sin(math.radians(fish["phase"]*1.5)) * fish["body_sway"]
        # 用多边形绘制鱼的身体、尾巴、鱼鳍
        points = self.calculate_fish_body_points(fish, vert_offset, body_sway)
        body = self.canvas.create_polygon(points, fill=fish["color"])
        # ...绘制眼睛和鱼鳍
    

  • 小鱼的「游动秘籍」
    通过 sin 函数计算「垂直波动」和「身体摆动」,让鱼像真实一样左右摇摆前进。
    尾巴和鱼鳍的角度随「相位」参数动态变化,模拟游动时的节奏感。
  • python

    运行

    def update_fish(self):
        for fish in self.fish:
            fish["x"] += fish["speed"] * fish["direction"]  # 水平移动
            fish["y"] += math.sin(...) * fish["vertical_speed"]  # 上下波动
            # 边界检测:碰到屏幕边缘就掉头
            if fish["x"] > 1024: fish["x"] = -100; fish["direction"] = -1
            # ...恐惧逃离、寻找食物等逻辑
    

  • 小鱼的「生存逻辑」
  • 恐惧系统:靠近鲨鱼时「恐惧等级」上升,会加速逃离;
  • 饥饿系统:饿了会随机转向找食物;
  • 随机行为:偶尔突然改变方向,增加真实感。
  • 🌊 第五章:顶级猎手的威胁(鲨鱼的捕猎逻辑)

    python

    运行

    def find_target_fish(self, shark):
        potential_targets = []
        for fish in self.fish:
            # 计算距离和方向,只选鲨鱼「正前方」的鱼
            if (dx>0 and shark["direction"]==1) or (dx<0 and shark["direction"]==-1):
                # 优先选择离得近、体型小的鱼
                priority = (1 - fish["size"]) * (1 - distance/300)
                potential_targets.append((fish, priority))
        # 按优先级排序,选第一条鱼作为目标
    
  • 鲨鱼的「捕猎策略」
    不会盲目追击,而是优先攻击「正前方」且「距离合适」的小鱼,模拟真实捕食行为。
  • python

    运行

    def shark_attack(self, shark, fish):
        # 移除被吃掉的鱼
        self.fish.remove(fish)
        self.canvas.delete(fish["body"])
        # 鲨鱼饱食一顿,饥饿值下降
        shark["hunger"] = max(0, shark["hunger"] - 200)
        # 生成气泡表示扰动
        for _ in range(10):
            self.bubbles.append(...)
    

  • 攻击瞬间的「视觉反馈」
    鱼被吃时会消失,同时生成大量气泡,增强画面冲击力。
  • 🌊 第六章:互动与控制(按钮与键盘操作)

    python

    运行

    def create_controls(self):
        control_frame = ttk.Frame(self.root)
        ttk.Button(control_frame, text="添加鱼", command=lambda: self.create_fish(3)).grid(...)
        # 键盘绑定:空格加鱼,s加鲨鱼,r重置
        self.root.bind("<space>", lambda e: self.create_fish(1))
    

  • 玩家的「上帝之手」
    通过按钮或键盘快速添加鱼 / 鲨鱼,观察生态平衡变化;
    点击「重置」会清空场景,重新生成初始生态系统。
  • 🌊 第七章:永不停止的循环(主动画逻辑)

    python

    运行

    def animate(self):
        if not self.is_running: return
        # 更新所有元素的位置和状态
        self.update_fish()
        self.update_sharks()
        self.update_bubbles()
        self.animate_seaweed()
        self.update_waves()
        # 每30毫秒调用一次自身,形成动画循环
        self.root.after(30, self.animate)
    

  • 海洋的「心跳」
    animate 方法像「时间齿轮」,不断刷新所有元素的状态,配合 root.after 实现流畅动画(约 33 帧 / 秒)。
  • 🚀 最后:运行与探索

    python

    运行

    if __name__ == "__main__":
        root = tk.Tk()
        app = MarineEcosystem(root)
        root.mainloop()
    
  • 启动指令
    这段代码是「启动海洋世界的钥匙」,运行后会看到一个动态的海洋场景:
  • 小鱼群左右穿梭,尾巴摆动带起涟漪;
  • 鲨鱼缓缓游动,靠近时小鱼会惊慌逃窜;
  • 海草随水流摆动,气泡从海底不断上浮。
  • 💡 给的探索建议:

    1. 点击「添加鲨鱼」按钮,观察鱼群如何躲避;
    2. 按下 r 键重置场景,看看初始生态是否平衡;
    3. 修改 create_fish 中的 speed 参数,让鱼游得更快或更慢;
    4. 在 generate_fish_color 中添加新颜色,创造彩虹鱼!

    看到这里;觉得有趣的朋友可以点赞+收藏,点赞破300!,笔者后续会更新强化版本;

    这个程序不仅是代码练习,更是「用代码模拟真实世界」的入门案例,通过调整参数和逻辑,你可以创造出独一无二的海洋生态系统~ 🌊🐟🦈

     完整代码如下:

    import tkinter as tk
    import random
    import math
    from tkinter import ttk
    
    
    class MarineEcosystem:
        def __init__(self, root):
            self.root = root
            self.root.title("海洋生态系统")
            self.root.geometry("1024x768")
    
            # 屏幕适配
            self.screen_width = self.root.winfo_screenwidth()
            self.screen_height = self.root.winfo_screenheight()
    
            # 创建画布
            self.canvas = tk.Canvas(root, width=self.screen_width, height=self.screen_height,
                                    bg="#000635", highlightthickness=0)
            self.canvas.pack(fill=tk.BOTH, expand=True)
    
            # 初始化元素
            self.fish = []
            self.sharks = []
            self.bubbles = []
            self.seaweed = []
            self.waves = []
            self.food = []
    
            # 控制变量
            self.is_running = True
            self.wave_phase = 0
            self.current_fish_count = 15
            self.current_shark_count = 2
    
            # 创建界面控件
            self.create_controls()
    
            # 初始化生态系统
            self.create_background()
            self.create_seaweed(8)
            self.create_waves()
            self.create_fish(self.current_fish_count)
            self.create_sharks(self.current_shark_count)
    
            # 启动动画循环
            self.animate()
    
        def create_controls(self):
            """创建控制面板"""
            control_frame = ttk.Frame(self.root)
            control_frame.place(x=10, y=10)
    
            ttk.Button(control_frame, text="添加鱼", command=lambda: self.create_fish(3)).grid(row=0, column=0, padx=5)
            ttk.Button(control_frame, text="添加鲨鱼", command=lambda: self.create_sharks(1)).grid(row=0, column=1, padx=5)
            ttk.Button(control_frame, text="重置场景", command=self.reset_scene).grid(row=0, column=2, padx=5)
    
            # 绑定键盘事件
            self.root.bind("<space>", lambda e: self.create_fish(1))
            self.root.bind("s", lambda e: self.create_sharks(1))
            self.root.bind("r", lambda e: self.reset_scene())
    
        def create_background(self):
            """创建渐变海洋背景"""
            for i in range(0, 100):
                gradient = 35 + int(20 * math.sin(i * 0.1))
                self.canvas.create_rectangle(
                    0, i * 10,
                    self.screen_width, (i + 1) * 10,
                    fill=f"#0006{gradient:02x}", outline=""
                )
    
        def create_waves(self):
            """创建水波效果层"""
            wave_colors = ["#1a4580", "#1a2f80", "#1a1f80"]
            for i in range(3):
                self.waves.append({
                    "path": self.canvas.create_line(
                        0, 0, 0, 0,
                        fill=wave_colors[i], width=3, smooth=True
                    ),
                    "points": [],
                    "speed": 0.5 + i * 0.2
                })
    
        def update_waves(self):
            """更新动态水波效果"""
            self.wave_phase = (self.wave_phase + 2) % 360
            for wave in self.waves:
                points = []
                for x in range(0, self.screen_width + 50, 50):
                    y = self.screen_height * 0.2 + math.sin(
                        math.radians(x / 4 + self.wave_phase * wave["speed"])
                    ) * 15
                    points.extend([x, y])
                wave["points"] = points
                self.canvas.coords(wave["path"], *points)
    
        def create_fish(self, count):
            """创建多条特征鱼"""
            for _ in range(count):
                size = random.uniform(0.6, 1.8)
                direction = random.choice([-1, 1])
    
                fish = {
                    "body": None, "tail": None, "eye": None, "dorsal_fin": None, "pectoral_fin": None,
                    "x": random.uniform(100, self.screen_width - 100),
                    "y": random.uniform(100, self.screen_height - 100),
                    "size": size,
                    "color": self.generate_fish_color(),
                    "direction": direction,
                    "speed": random.uniform(1.5, 4.0),
                    "wave_amp": random.uniform(1.0, 3.5),
                    "wave_freq": random.uniform(0.03, 0.12),
                    "vertical_speed": random.uniform(0.3, 0.8),
                    "phase": random.uniform(0, 360),
                    "tail_amp": random.uniform(20, 40),
                    "body_sway": random.uniform(0.5, 1.5),
                    "fin_phase": random.uniform(0, 360),
                    "color_variant": random.uniform(0.8, 1.2),
                    "type": random.choice(["normal", "fast", "slow"]),  # 鱼的类型
                    "fear_level": 0,  # 恐惧等级(受鲨鱼影响)
                    "last_direction_change": 0,  # 上次改变方向的时间
                    "hunger": 0,  # 饥饿值
                    "max_hunger": 300,  # 最大饥饿值
                    "is_hungry": False,  # 是否饥饿
                    "food_timer": 0,  # 寻找食物计时器
                    "target_food": None,  # 目标食物
                }
    
                # 根据鱼的类型调整参数
                if fish["type"] == "fast":
                    fish["speed"] *= 1.5
                    fish["wave_freq"] *= 1.3
                elif fish["type"] == "slow":
                    fish["speed"] *= 0.7
                    fish["wave_amp"] *= 1.5
    
                self.fish.append(fish)
    
        def generate_fish_color(self):
            """生成更逼真的鱼体颜色"""
            base_color = random.choice([
                # 蓝色系
                (0x2a, 0x6d, 0xa8), (0x1a, 0x93, 0xca), (0x2d, 0xa8, 0xc9),
                # 绿色系
                (0x32, 0xa8, 0x52), (0x2d, 0xc9, 0x88), (0x47, 0xc9, 0x2d),
                # 橙色系
                (0xc9, 0x75, 0x2d), (0xc9, 0xa6, 0x2d), (0xc9, 0x2d, 0x2d),
                # 银色系
                (0xa8, 0xa8, 0xa8), (0x8a, 0x9e, 0xb3), (0x73, 0x86, 0x99)
            ])
    
            # 添加轻微变化
            r = min(255, max(0, base_color[0] + random.randint(-30, 30)))
            g = min(255, max(0, base_color[1] + random.randint(-30, 30)))
            b = min(255, max(0, base_color[2] + random.randint(-30, 30)))
    
            return f"#{r:02x}{g:02x}{b:02x}"
    
        def create_sharks(self, count):
            """创建鲨鱼"""
            for _ in range(count):
                size = random.uniform(2.0, 3.5)
                direction = random.choice([-1, 1])
    
                shark = {
                    "body": None, "tail": None, "eye": None, "dorsal_fin": None, "pectoral_fin": None, "teeth": None,
                    "x": random.uniform(100, self.screen_width - 100),
                    "y": random.uniform(50, self.screen_height * 0.7),  # 鲨鱼倾向于在中层水域
                    "size": size,
                    "color": self.generate_shark_color(),
                    "direction": direction,
                    "speed": random.uniform(3.0, 6.0),
                    "wave_amp": random.uniform(0.5, 1.5),
                    "wave_freq": random.uniform(0.02, 0.06),
                    "vertical_speed": random.uniform(0.2, 0.5),
                    "phase": random.uniform(0, 360),
                    "tail_amp": random.uniform(25, 45),
                    "body_sway": random.uniform(0.8, 2.0),
                    "fin_phase": random.uniform(0, 360),
                    "hunger": 0,  # 饥饿值
                    "max_hunger": 500,  # 最大饥饿值
                    "is_hunting": False,  # 是否在捕猎
                    "target_fish": None,  # 目标鱼
                    "hunger_timer": random.randint(0, 200),  # 饥饿计时器
                    "attack_cooldown": 0,  # 攻击冷却
                }
    
                self.sharks.append(shark)
    
        def generate_shark_color(self):
            """生成鲨鱼颜色"""
            # 鲨鱼主要是灰色到蓝灰色
            base_color = random.randint(0x70, 0x90)
            belly_color = random.randint(0xe0, 0xf0)
    
            return {
                "top": f"#{base_color:02x}{base_color:02x}{base_color:02x}",
                "bottom": f"#{belly_color:02x}{belly_color:02x}{belly_color:02x}",
                "fin": f"#{(base_color - 20):02x}{(base_color - 20):02x}{(base_color - 20):02x}",
                "eye": "#ffffff",
                "pupil": "#000000",
                "teeth": "#ffffff"
            }
    
        def draw_fish(self, fish):
            """绘制单条鱼(带动态光影效果)"""
            # 删除旧图形
            for part in ["body", "tail", "eye", "dorsal_fin", "pectoral_fin"]:
                if fish[part]:
                    self.canvas.delete(fish[part])
    
            # 计算动态位置和变形
            fish["phase"] += fish["wave_freq"] * 10
            fish["fin_phase"] += fish["wave_freq"] * 15
    
            # 垂直波动
            vert_offset = math.sin(math.radians(fish["phase"])) * fish["wave_amp"] * 10
    
            # 鱼体变形参数
            body_sway = math.sin(math.radians(fish["phase"] * 1.5)) * fish["body_sway"]
            body_width = 40 * fish["size"]
            body_height = 20 * fish["size"]
    
            # 创建鱼体
            points = self.calculate_fish_body_points(fish, vert_offset, body_sway)
            body = self.canvas.create_polygon(points, fill=fish["color"], outline="")
    
            # 创建鱼尾
            tail_angle = math.sin(math.radians(fish["phase"] * 2)) * fish["tail_amp"]
            tail_points = self.calculate_tail_points(fish, vert_offset, tail_angle)
            tail = self.canvas.create_polygon(tail_points, fill=fish["color"], outline="")
    
            # 创建背鳍
            dorsal_fin_angle = math.sin(math.radians(fish["fin_phase"])) * 15
            dorsal_fin_points = self.calculate_dorsal_fin_points(fish, vert_offset, dorsal_fin_angle)
            dorsal_fin = self.canvas.create_polygon(dorsal_fin_points, fill=fish["color"], outline="")
    
            # 创建胸鳍
            pectoral_fin_angle = math.sin(math.radians(fish["fin_phase"] * 1.2)) * 25
            pectoral_fin_points = self.calculate_pectoral_fin_points(fish, vert_offset, pectoral_fin_angle)
            pectoral_fin = self.canvas.create_polygon(pectoral_fin_points, fill=fish["color"], outline="")
    
            # 创建眼睛
            eye_size = 5 * fish["size"]
            eye = self.canvas.create_oval(
                fish["x"] - 25 * fish["size"] * fish["direction"] - eye_size,
                fish["y"] + vert_offset - 8 * fish["size"] - eye_size,
                fish["x"] - 25 * fish["size"] * fish["direction"] + eye_size,
                fish["y"] + vert_offset - 8 * fish["size"] + eye_size,
                fill="#ffffff", outline=""
            )
    
            # 添加瞳孔
            pupil_size = eye_size * 0.5
            pupil = self.canvas.create_oval(
                fish["x"] - 25 * fish["size"] * fish["direction"] - pupil_size + fish["direction"] * 1.5,
                fish["y"] + vert_offset - 8 * fish["size"] - pupil_size,
                fish["x"] - 25 * fish["size"] * fish["direction"] + pupil_size + fish["direction"] * 1.5,
                fish["y"] + vert_offset - 8 * fish["size"] + pupil_size,
                fill="#000000", outline=""
            )
    
            fish.update({"body": body, "tail": tail, "eye": eye, "dorsal_fin": dorsal_fin, "pectoral_fin": pectoral_fin})
            return body, tail, eye, dorsal_fin, pectoral_fin
    
        def draw_shark(self, shark):
            """绘制鲨鱼"""
            # 删除旧图形
            for part in ["body", "tail", "eye", "dorsal_fin", "pectoral_fin", "teeth"]:
                if shark[part]:
                    self.canvas.delete(shark[part])
    
            # 计算动态位置和变形
            shark["phase"] += shark["wave_freq"] * 10
            shark["fin_phase"] += shark["wave_freq"] * 15
    
            # 垂直波动
            vert_offset = math.sin(math.radians(shark["phase"])) * shark["wave_amp"] * 10
    
            # 鲨鱼身体变形参数
            body_sway = math.sin(math.radians(shark["phase"] * 1.2)) * shark["body_sway"]
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            # 创建鲨鱼身体(分为上部和下部,模拟鲨鱼的颜色分布)
            top_points = self.calculate_shark_body_points(shark, vert_offset, body_sway, True)
            bottom_points = self.calculate_shark_body_points(shark, vert_offset, body_sway, False)
    
            body_top = self.canvas.create_polygon(top_points, fill=shark["color"]["top"], outline="")
            body_bottom = self.canvas.create_polygon(bottom_points, fill=shark["color"]["bottom"], outline="")
    
            # 创建鱼尾
            tail_angle = math.sin(math.radians(shark["phase"] * 1.8)) * shark["tail_amp"]
            tail_points = self.calculate_shark_tail_points(shark, vert_offset, tail_angle)
            tail = self.canvas.create_polygon(tail_points, fill=shark["color"]["top"], outline="")
    
            # 创建背鳍
            dorsal_fin_angle = math.sin(math.radians(shark["fin_phase"])) * 10
            dorsal_fin_points = self.calculate_shark_dorsal_fin_points(shark, vert_offset, dorsal_fin_angle)
            dorsal_fin = self.canvas.create_polygon(dorsal_fin_points, fill=shark["color"]["fin"], outline="")
    
            # 创建胸鳍
            pectoral_fin_angle = math.sin(math.radians(shark["fin_phase"] * 1.2)) * 20
            pectoral_fin_points = self.calculate_shark_pectoral_fin_points(shark, vert_offset, pectoral_fin_angle)
            pectoral_fin = self.canvas.create_polygon(pectoral_fin_points, fill=shark["color"]["fin"], outline="")
    
            # 创建眼睛
            eye_size = 8 * shark["size"]
            eye = self.canvas.create_oval(
                shark["x"] - 35 * shark["size"] * shark["direction"] - eye_size,
                shark["y"] + vert_offset - 12 * shark["size"] - eye_size,
                shark["x"] - 35 * shark["size"] * shark["direction"] + eye_size,
                shark["y"] + vert_offset - 12 * shark["size"] + eye_size,
                fill=shark["color"]["eye"], outline=""
            )
    
            # 添加瞳孔
            pupil_size = eye_size * 0.6
            pupil = self.canvas.create_oval(
                shark["x"] - 35 * shark["size"] * shark["direction"] - pupil_size + shark["direction"] * 2,
                shark["y"] + vert_offset - 12 * shark["size"] - pupil_size,
                shark["x"] - 35 * shark["size"] * shark["direction"] + pupil_size + shark["direction"] * 2,
                shark["y"] + vert_offset - 12 * shark["size"] + pupil_size,
                fill=shark["color"]["pupil"], outline=""
            )
    
            # 创建牙齿
            teeth = self.draw_shark_teeth(shark, vert_offset)
    
            shark.update({"body": body_top, "tail": tail, "eye": eye, "dorsal_fin": dorsal_fin,
                          "pectoral_fin": pectoral_fin, "teeth": teeth})
            return body_top, body_bottom, tail, eye, dorsal_fin, pectoral_fin, teeth
    
        def calculate_fish_body_points(self, fish, vert_offset, sway):
            """计算鱼体多边形的点"""
            body_width = 40 * fish["size"]
            body_height = 20 * fish["size"]
    
            head_offset = 10 * fish["size"] * sway
            tail_offset = 5 * fish["size"] * sway
    
            points = [
                fish["x"] + body_width * fish["direction"] + head_offset, fish["y"] + vert_offset,
                fish["x"] + body_width * 0.5 * fish["direction"], fish["y"] + vert_offset - body_height,
                fish["x"] - body_width * 0.8 * fish["direction"] + tail_offset, fish["y"] + vert_offset - body_height * 0.6,
                fish["x"] - body_width * fish["direction"] + tail_offset, fish["y"] + vert_offset,
                fish["x"] - body_width * 0.8 * fish["direction"] + tail_offset, fish["y"] + vert_offset + body_height * 0.6,
                fish["x"] + body_width * 0.5 * fish["direction"], fish["y"] + vert_offset + body_height,
            ]
    
            return points
    
        def calculate_tail_points(self, fish, vert_offset, angle):
            """计算鱼尾动态坐标"""
            body_width = 40 * fish["size"]
            body_height = 20 * fish["size"]
    
            base_x = fish["x"] - body_width * fish["direction"]
            base_y = fish["y"] + vert_offset
    
            tail_length = body_width * 0.6
            tail_height = body_height * 0.7
    
            angle_rad = math.radians(angle)
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            tip_x = base_x - tail_length * fish["direction"] * cos_angle
            tip_y = base_y - tail_length * fish["direction"] * sin_angle
    
            left_x = base_x + tail_height * sin_angle
            left_y = base_y + tail_height * cos_angle
    
            right_x = base_x - tail_height * sin_angle
            right_y = base_y - tail_height * cos_angle
    
            return [base_x, base_y, left_x, left_y, tip_x, tip_y, right_x, right_y]
    
        def calculate_dorsal_fin_points(self, fish, vert_offset, angle):
            """计算背鳍坐标"""
            body_width = 40 * fish["size"]
            body_height = 20 * fish["size"]
    
            base_x = fish["x"] - body_width * 0.3 * fish["direction"]
            base_y = fish["y"] + vert_offset - body_height
    
            fin_length = body_height * 0.6
            fin_width = body_height * 0.2
    
            angle_rad = math.radians(angle)
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            tip_x = base_x + fin_length * sin_angle
            tip_y = base_y - fin_length * cos_angle
    
            return [base_x, base_y,
                    base_x - fin_width * fish["direction"], base_y + fin_width,
                    tip_x, tip_y,
                    base_x + fin_width * fish["direction"], base_y + fin_width]
    
        def calculate_pectoral_fin_points(self, fish, vert_offset, angle):
            """计算胸鳍坐标"""
            body_width = 40 * fish["size"]
            body_height = 20 * fish["size"]
    
            base_x = fish["x"] + body_width * 0.2 * fish["direction"]
            base_y = fish["y"] + vert_offset
    
            fin_length = body_height * 0.4
            fin_width = body_height * 0.2
    
            angle_rad = math.radians(angle * fish["direction"])
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            tip_x = base_x + fin_length * cos_angle * fish["direction"]
            tip_y = base_y + fin_length * sin_angle
    
            return [base_x, base_y,
                    base_x - fin_width * fish["direction"], base_y + fin_width,
                    tip_x, tip_y,
                    base_x - fin_width * fish["direction"], base_y - fin_width]
    
        def calculate_shark_body_points(self, shark, vert_offset, sway, is_top):
            """计算鲨鱼身体多边形的点"""
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            head_offset = 15 * shark["size"] * sway
            tail_offset = 8 * shark["size"] * sway
    
            # 顶部和底部身体使用不同的点集
            if is_top:
                points = [
                    shark["x"] + body_width * shark["direction"] + head_offset,
                    shark["y"] + vert_offset - body_height * 0.3,
                    shark["x"] + body_width * 0.6 * shark["direction"], shark["y"] + vert_offset - body_height,
                    shark["x"] - body_width * 0.8 * shark["direction"] + tail_offset,
                    shark["y"] + vert_offset - body_height * 0.7,
                    shark["x"] - body_width * shark["direction"] + tail_offset,
                    shark["y"] + vert_offset - body_height * 0.3,
                ]
            else:
                points = [
                    shark["x"] + body_width * shark["direction"] + head_offset,
                    shark["y"] + vert_offset + body_height * 0.3,
                    shark["x"] + body_width * 0.6 * shark["direction"], shark["y"] + vert_offset + body_height,
                    shark["x"] - body_width * 0.8 * shark["direction"] + tail_offset,
                    shark["y"] + vert_offset + body_height * 0.7,
                    shark["x"] - body_width * shark["direction"] + tail_offset,
                    shark["y"] + vert_offset + body_height * 0.3,
                ]
    
            return points
    
        def calculate_shark_tail_points(self, shark, vert_offset, angle):
            """计算鲨鱼尾动态坐标"""
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            base_x = shark["x"] - body_width * shark["direction"]
            base_y = shark["y"] + vert_offset
    
            tail_length = body_width * 0.7
            tail_height = body_height * 0.8
    
            angle_rad = math.radians(angle)
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            # 修正错误:使用shark["direction"]而不是fish["direction"]
            tip_x = base_x - tail_length * shark["direction"] * cos_angle
            tip_y = base_y - tail_length * shark["direction"] * sin_angle
    
            left_x = base_x + tail_height * sin_angle
            left_y = base_y + tail_height * cos_angle
    
            right_x = base_x - tail_height * sin_angle
            right_y = base_y - tail_height * cos_angle
    
            return [base_x, base_y, left_x, left_y, tip_x, tip_y, right_x, right_y]
    
        def calculate_shark_dorsal_fin_points(self, shark, vert_offset, angle):
            """计算鲨鱼背鳍坐标"""
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            base_x = shark["x"] - body_width * 0.4 * shark["direction"]
            base_y = shark["y"] + vert_offset - body_height
    
            fin_length = body_height * 0.8
            fin_width = body_height * 0.3
    
            angle_rad = math.radians(angle)
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            tip_x = base_x + fin_length * sin_angle
            tip_y = base_y - fin_length * cos_angle
    
            return [base_x, base_y,
                    base_x - fin_width * shark["direction"], base_y + fin_width,
                    tip_x, tip_y,
                    base_x + fin_width * shark["direction"], base_y + fin_width]
    
        def calculate_shark_pectoral_fin_points(self, shark, vert_offset, angle):
            """计算鲨鱼胸鳍坐标"""
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            base_x = shark["x"] + body_width * 0.3 * shark["direction"]
            base_y = shark["y"] + vert_offset
    
            fin_length = body_height * 0.5
            fin_width = body_height * 0.3
    
            angle_rad = math.radians(angle * shark["direction"])
            cos_angle = math.cos(angle_rad)
            sin_angle = math.sin(angle_rad)
    
            tip_x = base_x + fin_length * cos_angle * shark["direction"]
            tip_y = base_y + fin_length * sin_angle
    
            return [base_x, base_y,
                    base_x - fin_width * shark["direction"], base_y + fin_width,
                    tip_x, tip_y,
                    base_x - fin_width * shark["direction"], base_y - fin_width]
    
        def draw_shark_teeth(self, shark, vert_offset):
            """绘制鲨鱼牙齿"""
            body_width = 60 * shark["size"]
            body_height = 30 * shark["size"]
    
            teeth = []
            teeth_count = int(6 * shark["size"])  # 牙齿数量
            tooth_size = body_height * 0.15
    
            for i in range(teeth_count):
                # 计算每个牙齿的位置
                x_pos = shark["x"] + body_width * 0.9 * shark["direction"] - i * tooth_size * shark["direction"]
                y_top = shark["y"] + vert_offset - body_height * 0.3
                y_bottom = shark["y"] + vert_offset + body_height * 0.3
    
                # 上排牙齿
                tooth_top = self.canvas.create_polygon(
                    x_pos, y_top,
                    x_pos - tooth_size * 0.3 * shark["direction"], y_top + tooth_size,
                    x_pos + tooth_size * 0.3 * shark["direction"], y_top + tooth_size,
                    fill=shark["color"]["teeth"], outline=""
                )
    
                # 下排牙齿
                tooth_bottom = self.canvas.create_polygon(
                    x_pos, y_bottom,
                    x_pos - tooth_size * 0.3 * shark["direction"], y_bottom - tooth_size,
                    x_pos + tooth_size * 0.3 * shark["direction"], y_bottom - tooth_size,
                    fill=shark["color"]["teeth"], outline=""
                )
    
                teeth.append(tooth_top)
                teeth.append(tooth_bottom)
    
            return teeth
    
        def create_bubbles(self):
            """生成气泡"""
            x = random.randint(50, self.screen_width - 50)
            bubble = {
                "id": self.canvas.create_oval(x, self.screen_height, x + 8, self.screen_height + 8,
                                              fill="#80ffff", outline="white"),
                "speed": random.uniform(1.5, 3.0),
                "drift": random.uniform(-0.5, 0.5)
            }
            self.bubbles.append(bubble)
    
        def create_seaweed(self, count):
            """创建海草森林"""
            for _ in range(count):
                x = random.randint(50, self.screen_width - 50)
                height = random.randint(100, 300)
                segments = []
                for i in range(10):
                    seg = self.canvas.create_line(x, self.screen_height - i * 10,
                                                  x + random.randint(-5, 5), self.screen_height - (i + 1) * 10,
                                                  fill="#227700", width=3, smooth=True)
                    segments.append(seg)
                self.seaweed.append({
                    "segments": segments,
                    "base_x": x,
                    "height": height,
                    "phase": random.uniform(0, 360)
                })
    
        def animate_seaweed(self):
            """更新海草摆动动画"""
            for plant in self.seaweed:
                phase = plant["phase"]
                for i, seg in enumerate(plant["segments"]):
                    offset = math.sin(math.radians(phase + i * 20)) * 5
                    x1, y1, x2, y2 = self.canvas.coords(seg)
                    self.canvas.coords(seg,
                                       plant["base_x"] + offset,
                                       self.screen_height - i * 10,
                                       plant["base_x"] + offset * 0.8 + random.randint(-2, 2),
                                       self.screen_height - (i + 1) * 10)
                plant["phase"] += 0.5
    
        def animate(self):
            """主动画循环"""
            if not self.is_running:
                return
    
            # 更新所有元素
            self.update_fish()
            self.update_sharks()
            self.update_bubbles()
            self.animate_seaweed()
            self.update_waves()
    
            # 检测鲨鱼与鱼的交互
            self.detect_shark_fish_interaction()
    
            # 控制帧率
            self.root.after(30, self.animate)
    
        def update_fish(self):
            """更新鱼的位置和动画"""
            for fish in self.fish:
                fish["hunger"] += 1
                fish["food_timer"] += 1
    
                if fish["hunger"] > fish["max_hunger"] * 0.7:
                    fish["is_hungry"] = True
                else:
                    fish["is_hungry"] = False
    
                # 检测附近的鲨鱼,增加恐惧等级
                fish["fear_level"] = self.calculate_fear_level(fish)
    
                # 如果恐惧等级高,尝试逃离
                if fish["fear_level"] > 0.5:
                    self.escape_from_shark(fish)
                # 如果饥饿,寻找食物
                elif fish["is_hungry"] and fish["food_timer"] > 50:
                    self.find_food(fish)
                    fish["food_timer"] = 0
                # 正常游动行为
                else:
                    # 偶尔改变垂直方向
                    if random.random() < 0.01:
                        fish["vertical_speed"] = random.uniform(0.3, 0.8)
    
                    # 偶尔改变水平速度
                    if random.random() < 0.005:
                        fish["speed"] = random.uniform(1.5, 4.0)
    
                    # 偶尔改变方向
                    fish["last_direction_change"] += 1
                    if fish["last_direction_change"] > 100 and random.random() < 0.01:
                        fish["direction"] *= -1
                        fish["last_direction_change"] = 0
    
                # 边界检测
                if fish["x"] > self.screen_width + 100:
                    fish["x"] = -100
                    fish["direction"] = -1
                elif fish["x"] < -100:
                    fish["x"] = self.screen_width + 100
                    fish["direction"] = 1
    
                if fish["y"] < 50:
                    fish["y"] = 50
                    fish["vertical_speed"] = abs(fish["vertical_speed"])
                elif fish["y"] > self.screen_height - 50:
                    fish["y"] = self.screen_height - 50
                    fish["vertical_speed"] = -abs(fish["vertical_speed"])
    
                # 水平移动
                fish["x"] += fish["speed"] * fish["direction"]
                # 垂直波动
                fish["y"] += math.sin(math.radians(fish["phase"])) * fish["vertical_speed"] * 0.3
    
                self.draw_fish(fish)
    
        def update_sharks(self):
            """更新鲨鱼的位置和动画"""
            for shark in self.sharks:
                shark["hunger"] += 1
                shark["hunger_timer"] += 1
    
                if shark["hunger"] > shark["max_hunger"] * 0.6:
                    shark["is_hunting"] = True
                else:
                    shark["is_hunting"] = False
    
                if shark["attack_cooldown"] > 0:
                    shark["attack_cooldown"] -= 1
    
                # 如果在捕猎,寻找并追逐猎物
                if shark["is_hunting"] and not shark["target_fish"]:
                    self.find_target_fish(shark)
    
                # 如果有目标,追逐目标
                if shark["target_fish"] and shark["target_fish"] in self.fish:
                    self.chase_target_fish(shark)
                else:
                    # 正常游动行为
                    shark["target_fish"] = None
    
                    # 偶尔改变垂直方向
                    if random.random() < 0.005:
                        shark["vertical_speed"] = random.uniform(0.2, 0.5)
    
                    # 偶尔改变水平速度
                    if random.random() < 0.003:
                        shark["speed"] = random.uniform(3.0, 6.0)
    
                    # 偶尔改变方向
                    if random.random() < 0.002:
                        shark["direction"] *= -1
    
                # 边界检测
                if shark["x"] > self.screen_width + 150:
                    shark["x"] = -150
                    shark["direction"] = -1
                elif shark["x"] < -150:
                    shark["x"] = self.screen_width + 150
                    shark["direction"] = 1
    
                if shark["y"] < 30:
                    shark["y"] = 30
                    shark["vertical_speed"] = abs(shark["vertical_speed"])
                elif shark["y"] > self.screen_height * 0.8:
                    shark["y"] = self.screen_height * 0.8
                    shark["vertical_speed"] = -abs(shark["vertical_speed"])
    
                # 水平移动
                shark["x"] += shark["speed"] * shark["direction"]
                # 垂直波动
                shark["y"] += math.sin(math.radians(shark["phase"])) * shark["vertical_speed"] * 0.2
    
                self.draw_shark(shark)
    
        def calculate_fear_level(self, fish):
            """计算鱼对附近鲨鱼的恐惧等级"""
            fear_level = 0
    
            for shark in self.sharks:
                # 计算鱼和鲨鱼之间的距离
                dx = shark["x"] - fish["x"]
                dy = shark["y"] - fish["y"]
                distance = math.sqrt(dx * dx + dy * dy)
    
                # 如果鲨鱼在视野范围内(并且面向鱼)
                if distance < 200 + shark["size"] * 30 and shark["direction"] == (1 if dx > 0 else -1):
                    # 距离越近,恐惧等级越高
                    fear_level += (1 - min(1, distance / (200 + shark["size"] * 30))) * 0.7
    
                    # 如果鲨鱼正在捕猎,恐惧等级更高
                    if shark["is_hunting"]:
                        fear_level += 0.3
    
            return min(1, fear_level)
    
        def escape_from_shark(self, fish):
            """逃离最近的鲨鱼"""
            nearest_shark = None
            min_distance = float('inf')
    
            for shark in self.sharks:
                dx = shark["x"] - fish["x"]
                dy = shark["y"] - fish["y"]
                distance = math.sqrt(dx * dx + dy * dy)
    
                if distance < min_distance:
                    min_distance = distance
                    nearest_shark = shark
    
            if nearest_shark:
                # 根据鲨鱼的位置决定逃跑方向
                if nearest_shark["x"] > fish["x"]:
                    fish["direction"] = -1  # 向左逃跑
                else:
                    fish["direction"] = 1  # 向右逃跑
    
                # 增加逃跑速度
                fish["speed"] = min(5.0, fish["speed"] * (1 + fish["fear_level"] * 0.5))
    
                # 随机垂直移动,增加不可预测性
                fish["vertical_speed"] = random.uniform(0.5, 1.2)
    
        def find_target_fish(self, shark):
            """寻找鲨鱼的目标鱼"""
            potential_targets = []
    
            for fish in self.fish:
                dx = fish["x"] - shark["x"]
                dy = fish["y"] - shark["y"]
                distance = math.sqrt(dx * dx + dy * dy)
    
                # 只考虑在鲨鱼前方且距离适中的鱼
                if (dx > 0 and shark["direction"] == 1) or (dx < 0 and shark["direction"] == -1):
                    if distance < 300 + shark["size"] * 20:
                        # 更可能选择较小的鱼
                        priority = (1 - fish["size"] / 2) * (1 - distance / (300 + shark["size"] * 20))
                        potential_targets.append((fish, priority))
    
            if potential_targets:
                # 按优先级排序,选择最高优先级的鱼
                potential_targets.sort(key=lambda x: x[1], reverse=True)
                shark["target_fish"] = potential_targets[0][0]
    
        def chase_target_fish(self, shark):
            """追逐目标鱼"""
            target = shark["target_fish"]
    
            if not target:
                return
    
            # 调整鲨鱼方向朝向目标
            if target["x"] > shark["x"] and shark["direction"] != 1:
                shark["direction"] = 1
            elif target["x"] < shark["x"] and shark["direction"] != -1:
                shark["direction"] = -1
    
            # 调整垂直位置
            if abs(target["y"] - shark["y"]) > 10:
                shark["vertical_speed"] = (target["y"] - shark["y"]) * 0.02
    
            # 增加捕猎速度
            shark["speed"] = max(4.0, min(7.0, shark["speed"] * 1.2))
    
        def find_food(self, fish):
            """寻找食物"""
            # 简化实现:随机方向游动一段时间寻找食物
            fish["direction"] = random.choice([-1, 1])
            fish["vertical_speed"] = random.uniform(0.4, 0.9)
            fish["speed"] = min(5.0, fish["speed"] * 1.1)
    
        def detect_shark_fish_interaction(self):
            """检测鲨鱼与鱼的交互"""
            for shark in self.sharks:
                if not shark["is_hunting"] or shark["attack_cooldown"] > 0:
                    continue
    
                if not shark["target_fish"] or shark["target_fish"] not in self.fish:
                    continue
    
                target = shark["target_fish"]
    
                # 计算距离
                dx = target["x"] - shark["x"]
                dy = target["y"] - shark["y"]
                distance = math.sqrt(dx * dx + dy * dy)
    
                # 如果鲨鱼足够接近目标并且面向目标
                if distance < 30 + target["size"] * 10 and (
                        (dx > 0 and shark["direction"] == 1) or (dx < 0 and shark["direction"] == -1)
                ):
                    # 鲨鱼攻击
                    self.shark_attack(shark, target)
    
        def shark_attack(self, shark, fish):
            """鲨鱼攻击鱼"""
            # 移除被吃掉的鱼
            if fish in self.fish:
                for part in ["body", "tail", "eye", "dorsal_fin", "pectoral_fin"]:
                    if fish[part]:
                        self.canvas.delete(fish[part])
                self.fish.remove(fish)
    
            # 重置鲨鱼状态
            shark["target_fish"] = None
            shark["hunger"] = max(0, shark["hunger"] - 200)
            shark["attack_cooldown"] = 50
    
            # 增加鲨鱼攻击后的速度
            shark["speed"] = min(8.0, shark["speed"] * 1.5)
    
            # 随机生成一些小气泡表示扰动
            for _ in range(10):
                x = fish["x"] + random.randint(-20, 20)
                y = fish["y"] + random.randint(-20, 20)
                bubble = {
                    "id": self.canvas.create_oval(x, y, x + 5, y + 5, fill="#80ffff", outline="white"),
                    "speed": random.uniform(1.0, 2.5),
                    "drift": random.uniform(-0.3, 0.3)
                }
                self.bubbles.append(bubble)
    
        def update_bubbles(self):
            """更新气泡位置"""
            for bubble in self.bubbles.copy():
                self.canvas.move(bubble["id"], bubble["drift"], -bubble["speed"])
                x, y, _, _ = self.canvas.coords(bubble["id"])
                if y < -50:
                    self.canvas.delete(bubble["id"])
                    self.bubbles.remove(bubble)
    
            # 随机生成新气泡
            if random.random() < 0.1:
                self.create_bubbles()
    
        def reset_scene(self):
            """重置整个场景"""
            self.canvas.delete("all")
            self.fish.clear()
            self.sharks.clear()
            self.bubbles.clear()
            self.seaweed.clear()
            self.waves.clear()
            self.create_background()
            self.create_seaweed(8)
            self.create_waves()
            self.create_fish(10)
            self.create_sharks(2)
    
    
    if __name__ == "__main__":
        root = tk.Tk()
        app = MarineEcosystem(root)
        root.mainloop()

    看到这里;点赞+收藏 支持一下作者吧!!作者初次尝试,系统还是初级版本;

    后续继续更新强化版本海洋系统,鱼群更多!效果更逼真!代码还是全部开放!!

    作者:zhonghuagongren

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python实现海洋鱼群动态系统绘制:详细步骤与完整代码分享

    发表回复