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()
这段代码是「启动海洋世界的钥匙」,运行后会看到一个动态的海洋场景:
💡 给的探索建议:
- 点击「添加鲨鱼」按钮,观察鱼群如何躲避;
- 按下
r键重置场景,看看初始生态是否平衡; - 修改
create_fish中的speed参数,让鱼游得更快或更慢; - 在
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