1. 内容概述与知识点设计
本篇博客将全面介绍使用PyVista进行三维战场仿真的完整技术栈。我们将从基础概念开始,逐步深入到高级特效实现,最终构建一个完整的动态海战仿真系统。
1.1 内容大纲
-
PyVista基础与环境配置
-
真实感地形生成技术
-
军事单位精细化建模
-
动态海洋效果实现
-
多视角相机控制系统
-
完整战场仿真系统集成
1.2 核心知识点
通过本博客,您将掌握以下关键技术点:
-
PyVista网格数据处理与可视化
-
基于噪声算法的地形生成
-
军事单位三维建模与动画
-
实时海洋波动模拟
-
相机路径规划与视角控制
-
大规模战场场景优化
2. PyVista技术深度解析
2.1 PyVista架构概述
PyVista是建立在VTK(Visualization Toolkit)之上的Python三维可视化库,它提供了简洁的API接口,同时保留了VTK的强大功能。PyVista的核心数据结构是网格(Mesh),它可以表示点云、曲面、体积等各种三维数据。
python
import pyvista as pv
import numpy as np
class PyVistaCoreConcepts:
"""PyVista核心概念详解"""
def demonstrate_mesh_concepts(self):
"""展示PyVista网格基本概念"""
# 创建基本几何体
sphere = pv.Sphere()
print(f"球体网格信息:")
print(f"点数: {sphere.n_points}")
print(f"单元数: {sphere.n_cells}")
print(f"边界范围: {sphere.bounds}")
# 访问点数据
points = sphere.points
print(f"前5个点坐标:\n{points[:5]}")
# 访问单元数据
cells = sphere.cells
print(f"单元连接信息:\n{cells[:10]}")
return sphere
2.2 PyVista绘图系统深度解析
PyVista的绘图系统基于Plotter类,它提供了丰富的可视化选项和交互功能。
python
class AdvancedPlotterSystem:
"""高级绘图器系统详解"""
def __init__(self):
self.plotter = None
def create_advanced_plotter(self):
"""创建高级绘图器"""
self.plotter = pv.Plotter(
window_size=[1600, 900],
lighting="three_lights",
multi_samples=8, # 多重采样抗锯齿
polygon_smoothing=True,
line_smoothing=True
)
# 设置高级渲染参数
self.plotter.enable_anti_aliasing('ssaa')
self.plotter.enable_depth_peeling()
return self.plotter
def setup_advanced_lighting(self):
"""设置高级光照系统"""
# 添加多个光源
self.plotter.add_light(pv.Light(
position=(5, 5, 5),
focal_point=(0, 0, 0),
color='white',
intensity=0.6
))
self.plotter.add_light(pv.Light(
position=(-5, -5, 5),
focal_point=(0, 0, 0),
color='blue',
intensity=0.3
))
3. 地形生成技术
3.1 多频噪声地形生成
使用多频率Perlin噪声生成高度真实的地形。
python
class RealisticTerrainGenerator:
"""真实感地形生成器"""
def __init__(self, size=1000, resolution=256):
self.size = size
self.resolution = resolution
self.terrain = None
def generate_perlin_noise(self, x, y, frequency=1.0, octaves=4):
"""生成Perlin噪声"""
value = 0.0
amplitude = 1.0
max_value = 0.0
for _ in range(octaves):
# 简化版Perlin噪声
noise_val = np.sin(frequency * x * 0.01 +
np.cos(frequency * y * 0.01)) * \
np.cos(frequency * y * 0.01 -
np.sin(frequency * x * 0.01))
value += amplitude * noise_val
max_value += amplitude
amplitude *= 0.5
frequency *= 2.0
return value / max_value
def create_realistic_terrain(self):
"""创建真实感地形"""
x = np.linspace(-self.size/2, self.size/2, self.resolution)
y = np.linspace(-self.size/2, self.size/2, self.resolution)
x, y = np.meshgrid(x, y)
# 多频率噪声叠加
z_low_freq = self.generate_perlin_noise(x, y, frequency=0.5, octaves=2)
z_med_freq = self.generate_perlin_noise(x, y, frequency=2.0, octaves=3) * 0.5
z_high_freq = self.generate_perlin_noise(x, y, frequency=8.0, octaves=4) * 0.2
# 组合噪声
z = z_low_freq * 200 + z_med_freq * 50 + z_high_freq * 10
# 添加侵蚀效果
z = self.apply_erosion_effect(z)
# 创建地形网格
self.terrain = pv.StructuredGrid(x, y, z)
# 计算地形属性
self.calculate_terrain_attributes()
return self.terrain
def apply_erosion_effect(self, z):
"""应用侵蚀效果"""
from scipy import ndimage
# 模拟水力侵蚀
slope = np.gradient(z)
erosion_mask = np.sqrt(slope[0]**2 + slope[1]**2) > 0.1
z[erosion_mask] -= 2.0
# 平滑处理
z = ndimage.gaussian_filter(z, sigma=1.0)
return z
def calculate_terrain_attributes(self):
"""计算地形属性"""
if self.terrain is None:
return
z = self.terrain.points[:, 2].reshape(self.resolution, self.resolution)
# 计算坡度
grad_x, grad_y = np.gradient(z)
slope = np.sqrt(grad_x**2 + grad_y**2)
self.terrain["slope"] = slope.flatten()
# 计算朝向(用于军事分析)
aspect = np.arctan2(grad_y, grad_x)
self.terrain["aspect"] = aspect.flatten()
# 计算通视性
self.terrain["visibility"] = self.calculate_visibility(z)
def calculate_visibility(self, elevation):
"""计算地形通视性"""
visibility = np.ones_like(elevation)
height, width = elevation.shape
# 简化的通视性计算
for i in range(10, height-10):
for j in range(10, width-10):
# 检查周围地形遮挡
local_region = elevation[i-5:i+5, j-5:j+5]
if elevation[i,j] < np.max(local_region) - 5:
visibility[i,j] = 0.3 # 低通视性
elif elevation[i,j] > np.max(local_region) + 10:
visibility[i,j] = 1.5 # 高通视性(制高点)
return visibility.flatten()
3.2 纹理映射与材质系统
为地形添加真实的纹理和材质。
python
class TerrainTextureSystem:
"""地形纹理系统"""
def __init__(self, terrain):
self.terrain = terrain
self.textures = {}
def create_texture_based_on_elevation(self):
"""基于高程创建纹理"""
elevation = self.terrain["elevation"]
slope = self.terrain["slope"]
# 基于高程和坡度的纹理映射
texture = np.zeros((len(elevation), 3)) # RGB纹理
for i, (elev, slp) in enumerate(zip(elevation, slope)):
if elev < 0: # 水下
texture[i] = [0.1, 0.2, 0.6] # 深蓝色
elif elev < 50: # 海滩
texture[i] = [0.9, 0.8, 0.6] # 沙色
elif slp > 0.3: # 陡坡
texture[i] = [0.4, 0.4, 0.4] # 岩石灰色
elif elev > 200: # 高山
texture[i] = [0.8, 0.8, 0.8] # 雪白色
else: # 平原
texture[i] = [0.3, 0.6, 0.3] # 绿色
self.terrain["texture"] = texture
def add_satellite_texture(self, texture_file=None):
"""添加卫星纹理"""
if texture_file is None:
# 创建模拟卫星纹理
self.create_simulated_satellite_texture()
else:
# 加载真实卫星纹理
texture = pv.read_texture(texture_file)
self.terrain.textures["satellite"] = texture
def create_simulated_satellite_texture(self):
"""创建模拟卫星纹理"""
# 基于地形属性生成模拟纹理
elevation = self.terrain["elevation"]
vegetation = np.sin(elevation * 0.1) * 0.5 + 0.5 # 模拟植被
texture_rgb = np.zeros((len(elevation), 3))
for i, (elev, veg) in enumerate(zip(elevation, vegetation)):
if elev < 0:
texture_rgb[i] = [0, 0, 0.5] # 水体
else:
# 植被覆盖度影响颜色
green = 0.3 + veg * 0.5
texture_rgb[i] = [0.1, green, 0.1]
self.terrain["satellite_texture"] = texture_rgb
4. 军事单位建模
4.1 海军舰船建模系统
创建各种类型的海军舰船模型。
python
class NavalUnitFactory:
"""海军单位工厂"""
@staticmethod
def create_destroyer(position, scale=1.0):
"""创建驱逐舰模型"""
# 舰体主体
hull = pv.Cylinder(
center=[0, 0, 0.1*scale],
direction=[1, 0, 0],
radius=0.3*scale,
height=2.0*scale
)
# 舰桥
bridge = pv.Cube(
center=[0.5*scale, 0, 0.8*scale],
x_length=0.4*scale,
y_length=0.6*scale,
z_length=0.8*scale
)
# 炮塔
turret = pv.Cylinder(
center=[-0.5*scale, 0, 0.4*scale],
direction=[0, 0, 1],
radius=0.15*scale,
height=0.2*scale
)
# 雷达
radar = pv.Cone(
center=[0, 0, 1.2*scale],
direction=[0, 0, 1],
height=0.3*scale,
radius=0.1*scale
)
# 组合所有部件
destroyer = hull + bridge + turret + radar
destroyer.translate(position)
return destroyer
@staticmethod
def create_aircraft_carrier(position, scale=1.0):
"""创建航空母舰模型"""
# 甲板
deck = pv.Cube(
center=[0, 0, 0.2*scale],
x_length=4.0*scale,
y_length=1.0*scale,
z_length=0.1*scale
)
# 岛式上层建筑
island = pv.Cube(
center=[1.0*scale, 0, 0.6*scale],
x_length=0.8*scale,
y_length=0.3*scale,
z_length=0.8*scale
)
# 飞行甲板标记
runway_lines = []
for i in range(5):
line = pv.Cube(
center=[-1.0*scale + i*0.5*scale, 0, 0.21*scale],
x_length=0.02*scale,
y_length=0.8*scale,
z_length=0.02*scale
)
runway_lines.append(line)
carrier = deck + island
for line in runway_lines:
carrier += line
carrier.translate(position)
return carrier
@staticmethod
def create_submarine(position, scale=1.0):
"""创建潜艇模型"""
# 艇体(椭球体)
hull = pv.ParametricEllipsoid(
0.8*scale, 0.2*scale, 0.2*scale
)
# 指挥塔
sail = pv.Cylinder(
center=[0, 0, 0.15*scale],
direction=[0, 0, 1],
radius=0.1*scale,
height=0.3*scale
)
submarine = hull + sail
submarine.translate(position)
return submarine
4.2 军事单位管理系统
实现军事单位的创建、管理和动画控制。
python
class MilitaryUnitManager:
"""军事单位管理器"""
def __init__(self, plotter):
self.plotter = plotter
self.units = []
self.unit_meshes = {}
self.animations = []
def add_naval_unit(self, unit_type, position, faction="blue", scale=1.0):
"""添加海军单位"""
if unit_type == "destroyer":
unit_mesh = NavalUnitFactory.create_destroyer(position, scale)
elif unit_type == "carrier":
unit_mesh = NavalUnitFactory.create_aircraft_carrier(position, scale)
elif unit_type == "submarine":
unit_mesh = NavalUnitFactory.create_submarine(position, scale)
else:
unit_mesh = NavalUnitFactory.create_destroyer(position, scale)
# 设置单位颜色
color = "darkblue" if faction == "blue" else "darkred"
actor = self.plotter.add_mesh(
unit_mesh,
color=color,
smooth_shading=True,
metallic=0.3,
roughness=0.7
)
unit_info = {
'id': len(self.units),
'type': unit_type,
'faction': faction,
'position': np.array(position),
'mesh': unit_mesh,
'actor': actor,
'scale': scale,
'waypoints': [],
'current_speed': 0.0
}
self.units.append(unit_info)
self.unit_meshes[unit_info['id']] = unit_mesh
return unit_info
def add_unit_waypoints(self, unit_id, waypoints):
"""为单位添加路径点"""
if unit_id < len(self.units):
self.units[unit_id]['waypoints'] = waypoints
def update_units_movement(self, delta_time):
"""更新单位移动"""
for unit in self.units:
if len(unit['waypoints']) > 0:
self.move_toward_waypoint(unit, delta_time)
def move_toward_waypoint(self, unit, delta_time):
"""向路径点移动"""
current_pos = unit['position']
target_pos = unit['waypoints'][0]
direction = target_pos - current_pos
distance = np.linalg.norm(direction)
if distance < 1.0: # 到达路径点
unit['waypoints'].pop(0)
if len(unit['waypoints']) == 0:
unit['current_speed'] = 0.0
return
# 标准化方向向量
if distance > 0:
direction = direction / distance
# 根据单位类型设置速度
max_speed = 10.0 if unit['type'] == 'destroyer' else 5.0
acceleration = 2.0
# 加速或减速
if distance > 10.0:
unit['current_speed'] = min(unit['current_speed'] + acceleration * delta_time, max_speed)
else:
unit['current_speed'] = max(unit['current_speed'] - acceleration * delta_time, 0)
# 计算移动距离
move_distance = unit['current_speed'] * delta_time
if move_distance > distance:
move_distance = distance
# 更新位置
new_position = current_pos + direction * move_distance
unit['position'] = new_position
# 更新网格位置
translation = new_position - current_pos
unit['mesh'].translate(translation, inplace=True)
5. 动态海洋效果实现
5.1 实时海面波动模拟
使用Gerstner波算法创建真实的海面波动效果。
python
class DynamicOceanSystem:
"""动态海洋系统"""
def __init__(self, size=2000, resolution=200):
self.size = size
self.resolution = resolution
self.ocean_grid = None
self.time = 0.0
self.waves = []
# 初始化波参数
self.init_wave_parameters()
def init_wave_parameters(self):
"""初始化波参数"""
self.waves = [
{'amplitude': 2.0, 'wavelength': 50.0, 'direction': [1.0, 0.5], 'speed': 2.0},
{'amplitude': 1.5, 'wavelength': 30.0, 'direction': [0.7, -0.3], 'speed': 3.0},
{'amplitude': 1.0, 'wavelength': 20.0, 'direction': [-0.5, 0.8], 'speed': 1.5}
]
# 归一化方向向量
for wave in self.waves:
direction = np.array(wave['direction'])
wave['direction'] = direction / np.linalg.norm(direction)
def create_ocean_grid(self):
"""创建海洋网格"""
x = np.linspace(-self.size/2, self.size/2, self.resolution)
y = np.linspace(-self.size/2, self.size/2, self.resolution)
x, y = np.meshgrid(x, y)
z = np.zeros_like(x)
self.ocean_grid = pv.StructuredGrid(x, y, z)
self.ocean_grid["elevation"] = z.flatten()
return self.ocean_grid
def update_ocean_waves(self, delta_time):
"""更新海洋波动"""
if self.ocean_grid is None:
return
self.time += delta_time
x = self.ocean_grid.points[:, 0].reshape(self.resolution, self.resolution)
y = self.ocean_grid.points[:, 1].reshape(self.resolution, self.resolution)
z = np.zeros_like(x)
# 叠加多个波
for wave in self.waves:
k = 2 * np.pi / wave['wavelength'] # 波数
omega = wave['speed'] * k # 角频率
# 计算波方向上的位置
dir_array = np.array(wave['direction'])
dot_product = x * dir_array[0] + y * dir_array[1]
# Gerstner波公式
wave_height = wave['amplitude'] * np.sin(k * dot_product - omega * self.time)
z += wave_height
# 添加波峰尖锐效果
steepness = 0.3
z += steepness * wave['amplitude'] * np.cos(k * dot_product - omega * self.time)
# 更新网格高度
self.ocean_grid.points[:, 2] = z.flatten()
self.ocean_grid["elevation"] = z.flatten()
def create_foam_effect(self, threshold=0.5):
"""创建泡沫效果"""
if self.ocean_grid is None:
return
elevation = np.abs(self.ocean_grid["elevation"])
foam_mask = elevation > threshold
# 创建泡沫纹理
foam_texture = np.zeros((len(elevation), 3))
foam_texture[foam_mask] = [1.0, 1.0, 1.0] # 白色泡沫
foam_texture[~foam_mask] = [0.1, 0.2, 0.8] # 蓝色海水
self.ocean_grid["foam_texture"] = foam_texture
def add_ocean_to_plotter(self, plotter):
"""将海洋添加到绘图器"""
if self.ocean_grid is not None:
plotter.add_mesh(
self.ocean_grid,
scalars="elevation",
cmap="ocean",
opacity=0.8,
smooth_shading=True,
show_edges=False
)
5.2 海洋材质与反射效果
增强海洋的视觉真实感。
python
class OceanMaterialSystem:
"""海洋材质系统"""
def __init__(self, ocean_grid):
self.ocean_grid = ocean_grid
def apply_advanced_material(self):
"""应用高级材质"""
# 创建法线贴图效果
self.create_normal_map()
# 创建反射效果
self.create_reflection_map()
def create_normal_map(self):
"""创建法线贴图"""
if self.ocean_grid is None:
return
elevation = self.ocean_grid["elevation"].reshape(-1, 1)
# 简化法线计算
points = self.ocean_grid.points
normals = np.zeros_like(points)
# 计算简单法线(实际应用需要更复杂的计算)
for i in range(1, len(points)-1):
if i % self.ocean_grid.dimensions[0] != 0: # 不是左边界
dx = points[i] - points[i-1]
else:
dx = points[i+1] - points[i]
if i >= self.ocean_grid.dimensions[0]: # 不是上边界
dy = points[i] - points[i-self.ocean_grid.dimensions[0]]
else:
dy = points[i+self.ocean_grid.dimensions[0]] - points[i]
# 计算法线
normal = np.cross(dx, dy)
if np.linalg.norm(normal) > 0:
normal = normal / np.linalg.norm(normal)
normals[i] = normal
self.ocean_grid["normals"] = normals
def create_reflection_map(self):
"""创建反射贴图"""
# 基于视角和法线计算反射强度
if hasattr(self.ocean_grid, 'normals'):
normals = self.ocean_grid["normals"]
# 简化反射计算
reflection = np.zeros(len(normals))
for i, normal in enumerate(normals):
# 假设光源在摄像机位置
reflection[i] = max(0, normal[2]) # 使用法线的Z分量
self.ocean_grid["reflection"] = reflection
6. 相机控制系统
6.1 多视角相机设置
实现多种相机视角控制模式。
python
class BattlefieldCameraSystem:
"""战场相机控制系统"""
def __init__(self, plotter):
self.plotter = plotter
self.camera_modes = {
'overview': self.set_overview_camera,
'first_person': self.set_first_person_camera,
'follow_unit': self.set_follow_unit_camera,
'free_flight': self.set_free_flight_camera
}
self.current_mode = 'overview'
self.follow_unit_id = None
def set_overview_camera(self, terrain_bounds):
"""设置俯视视角"""
# 计算地形中心
center = [
(terrain_bounds[0] + terrain_bounds[1]) / 2,
(terrain_bounds[2] + terrain_bounds[3]) / 2,
(terrain_bounds[4] + terrain_bounds[5]) / 2
]
# 设置相机位置(高空俯视)
camera_pos = [
center[0],
center[1],
terrain_bounds[5] * 3 # 高度的3倍
]
self.plotter.camera_position = [
camera_pos,
center,
(0, 0, 1) # 上方向向量
]
def set_first_person_camera(self, unit_position, unit_direction=None):
"""设置第一人称视角"""
if unit_direction is None:
unit_direction = [1, 0, 0] # 默认朝X轴方向
# 相机位置在单位上方
camera_pos = unit_position + [0, 0, 2] # 高度2米
# 看向单位朝向方向
focal_point = unit_position + unit_direction * 10
self.plotter.camera_position = [
camera_pos,
focal_point,
(0, 0, 1)
]
def set_follow_unit_camera(self, unit_position, unit_velocity=None):
"""设置跟随单位视角"""
if unit_velocity is None:
unit_velocity = [1, 0, 0]
# 归一化速度方向
speed = np.linalg.norm(unit_velocity)
if speed > 0:
unit_direction = unit_velocity / speed
else:
unit_direction = [1, 0, 0]
# 相机在单位后方上方
camera_offset = np.array(unit_direction) * -10 + [0, 0, 5]
camera_pos = unit_position + camera_offset
self.plotter.camera_position = [
camera_pos.tolist(),
unit_position,
(0, 0, 1)
]
def set_free_flight_camera(self, position, focal_point):
"""设置自由飞行视角"""
self.plotter.camera_position = [
position,
focal_point,
(0, 0, 1)
]
def create_camera_animation_path(self, start_pos, end_pos, num_points=100):
"""创建相机动画路径"""
# 创建贝塞尔曲线路径
t = np.linspace(0, 1, num_points)
# 控制点(在中间位置添加高度)
control_point = (start_pos + end_pos) / 2 + [0, 0, 50]
# 二次贝塞尔曲线
path_points = []
for i in range(num_points):
point = (1-t[i])**2 * start_pos + \
2*(1-t[i])*t[i] * control_point + \
t[i]**2 * end_pos
path_points.append(point)
return path_points
def animate_camera_along_path(self, path_points, duration=10):
"""沿路径动画相机"""
self.plotter.open_gif("camera_animation.gif", fps=30)
num_frames = duration * 30
frame_indices = np.linspace(0, len(path_points)-1, num_frames, dtype=int)
for i, frame_idx in enumerate(frame_indices):
if frame_idx < len(path_points):
# 设置相机位置
camera_pos = path_points[frame_idx]
# 看向路径上的下一个点
look_ahead_idx = min(frame_idx + 5, len(path_points)-1)
focal_point = path_points[look_ahead_idx]
self.set_free_flight_camera(camera_pos, focal_point)
self.plotter.write_frame()
self.plotter.close()
7. 战场仿真系统
7.1 主仿真引擎
集成所有组件的完整仿真系统。
python
class BattlefieldSimulationEngine:
"""战场仿真引擎"""
def __init__(self):
self.plotter = None
self.terrain = None
self.ocean_system = None
self.unit_manager = None
self.camera_system = None
self.is_running = False
self.last_time = None
def initialize_simulation(self):
"""初始化仿真系统"""
print("初始化战场仿真系统...")
# 创建高级绘图器
self.plotter = pv.Plotter(window_size=[1920, 1080])
self.plotter.set_background("skyblue", top="lightblue")
# 创建地形系统
terrain_gen = RealisticTerrainGenerator(size=2000, resolution=300)
self.terrain = terrain_gen.create_realistic_terrain()
# 创建海洋系统
self.ocean_system = DynamicOceanSystem(size=2000, resolution=250)
ocean_grid = self.ocean_system.create_ocean_grid()
# 创建单位管理器
self.unit_manager = MilitaryUnitManager(self.plotter)
# 创建相机系统
self.camera_system = BattlefieldCameraSystem(self.plotter)
# 添加地形到场景
self.plotter.add_mesh(
self.terrain,
scalars="elevation",
cmap="gist_earth",
opacity=0.9,
smooth_shading=True
)
# 添加海洋到场景
self.ocean_system.add_ocean_to_plotter(self.plotter)
# 部署军事单位
self.deploy_military_units()
# 设置初始相机视角
self.camera_system.set_overview_camera(self.terrain.bounds)
print("仿真系统初始化完成!")
def deploy_military_units(self):
"""部署军事单位"""
# 蓝方舰队
blue_fleet = [
{"type": "carrier", "position": [-500, -200, 0], "faction": "blue"},
{"type": "destroyer", "position": [-400, -100, 0], "faction": "blue"},
{"type": "destroyer", "position": [-400, -300, 0], "faction": "blue"},
{"type": "submarine", "position": [-600, -200, 0], "faction": "blue"}
]
# 红方舰队
red_fleet = [
{"type": "carrier", "position": [500, 200, 0], "faction": "red"},
{"type": "destroyer", "position": [400, 100, 0], "faction": "red"},
{"type": "destroyer", "position": [400, 300, 0], "faction": "red"},
{"type": "submarine", "position": [600, 200, 0], "faction": "red"}
]
# 部署单位
for unit_data in blue_fleet + red_fleet:
self.unit_manager.add_naval_unit(
unit_data["type"],
unit_data["position"],
unit_data["faction"],
scale=10.0
)
# 设置单位移动路径
self.set_unit_navigation_paths()
def set_unit_navigation_paths(self):
"""设置单位导航路径"""
# 蓝方单位路径
blue_paths = [
[(-500, -200, 0), (-300, -100, 0), (-100, -50, 0)], # 航母路径
[(-400, -100, 0), (-250, -50, 0), (-150, 0, 0)], # 驱逐舰1路径
[(-400, -300, 0), (-250, -200, 0), (-150, -100, 0)], # 驱逐舰2路径
]
# 红方单位路径
red_paths = [
[(500, 200, 0), (300, 150, 0), (100, 100, 0)], # 航母路径
[(400, 100, 0), (250, 50, 0), (150, 0, 0)], # 驱逐舰1路径
[(400, 300, 0), (250, 250, 0), (150, 200, 0)], # 驱逐舰2路径
]
# 为每个单位设置路径
for i, path in enumerate(blue_paths + red_paths):
if i < len(self.unit_manager.units):
self.unit_manager.add_unit_waypoints(i, path)
def run_simulation(self, duration=60):
"""运行仿真"""
print(f"开始仿真,持续时间: {duration}秒")
self.is_running = True
self.last_time = time.time()
start_time = time.time()
while self.is_running and (time.time() - start_time) < duration:
# 计算时间增量
current_time = time.time()
delta_time = current_time - self.last_time
self.last_time = current_time
# 更新系统
self.update_simulation(delta_time)
# 限制帧率
time.sleep(0.016) # 约60FPS
# 检查用户输入(退出条件)
if not self.plotter.iren.running:
self.is_running = False
print("仿真结束")
def update_simulation(self, delta_time):
"""更新仿真状态"""
# 更新海洋波动
self.ocean_system.update_ocean_waves(delta_time)
# 更新单位移动
self.unit_manager.update_units_movement(delta_time)
# 更新相机(如果处于跟随模式)
self.update_camera_follow()
# 刷新显示
self.plotter.update()
def update_camera_follow(self):
"""更新相机跟随"""
if (self.camera_system.current_mode == 'follow_unit' and
self.camera_system.follow_unit_id is not None):
unit_id = self.camera_system.follow_unit_id
if unit_id < len(self.unit_manager.units):
unit = self.unit_manager.units[unit_id]
self.camera_system.set_follow_unit_camera(
unit['position'],
unit.get('velocity', [1, 0, 0])
)
def interactive_control(self):
"""交互式控制"""
print("交互式控制模式")
print("快捷键:")
print(" O - 俯视视角")
print(" F - 第一人称视角")
print(" C - 跟随相机")
print(" R - 自由飞行")
print(" Q - 退出")
# 设置键盘回调
self.plotter.add_key_event("o", lambda: self.camera_system.set_overview_camera(self.terrain.bounds))
self.plotter.add_key_event("f", self.set_first_person_view)
self.plotter.add_key_event("c", self.set_follow_view)
self.plotter.add_key_event("r", self.set_free_view)
self.plotter.add_key_event("q", lambda: setattr(self, 'is_running', False))
def set_first_person_view(self):
"""设置第一人称视角"""
if len(self.unit_manager.units) > 0:
unit_pos = self.unit_manager.units[0]['position']
self.camera_system.set_first_person_camera(unit_pos)
self.camera_system.current_mode = 'first_person'
def set_follow_view(self):
"""设置跟随视角"""
if len(self.unit_manager.units) > 0:
self.camera_system.follow_unit_id = 0
self.camera_system.current_mode = 'follow_unit'
def set_free_view(self):
"""设置自由视角"""
self.camera_system.current_mode = 'free_flight'
def show_simulation(self):
"""显示仿真"""
self.interactive_control()
self.plotter.show()
7.2 完整可运行Demo
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PyVista三维战场仿真完整Demo
功能:真实地形生成、动态海洋、海军单位模拟、多视角控制
作者:基于用户反馈深度优化
日期:2026年1月18日
"""
import pyvista as pv
import numpy as np
import time
# 这里包含前面定义的所有类:
# RealisticTerrainGenerator, TerrainTextureSystem,
# NavalUnitFactory, MilitaryUnitManager,
# DynamicOceanSystem, OceanMaterialSystem,
# BattlefieldCameraSystem, BattlefieldSimulationEngine
def main():
"""主函数"""
print("=" * 60)
print("PyVista三维海战场仿真系统")
print("=" * 60)
try:
# 创建仿真引擎
simulator = BattlefieldSimulationEngine()
# 初始化仿真系统
simulator.initialize_simulation()
# 运行仿真(可选)
print("开始运行仿真...")
print("提示:使用鼠标进行交互查看")
print("快捷键: O-俯视, F-第一人称, C-跟随, R-自由飞行, Q-退出")
# 在单独的线程中运行仿真更新
import threading
simulation_thread = threading.Thread(
target=simulator.run_simulation,
args=(300,) # 运行300秒
)
simulation_thread.daemon = True
simulation_thread.start()
# 显示仿真界面
simulator.show_simulation()
print("仿真系统关闭")
except Exception as e:
print(f"错误: {str(e)}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
8. 总结与扩展建议
本博客详细介绍了使用PyVista进行三维战场仿真的完整技术栈。通过实现真实感地形生成、动态海洋效果、军事单位建模和高级相机控制,我们构建了一个功能丰富的海战仿真系统。
8.1 技术亮点总结
-
真实感渲染:多频率噪声地形、动态海洋波动、高级材质系统
-
军事仿真:精细化海军单位建模、路径规划、实时运动控制
-
交互体验:多视角相机系统、键盘交互、实时动画
-
系统架构:模块化设计、可扩展的仿真引擎
8.2 性能优化建议
对于大规模战场仿真,可以考虑以下优化策略:
-
使用层次细节(LOD)技术
-
实现数据分块加载
-
采用GPU加速计算
-
优化网格数据结构和算法
8.3 扩展方向
-
人工智能集成:添加单位AI决策系统
-
物理引擎:集成刚体物理和碰撞检测
-
网络功能:实现多机协同仿真
-
数据驱动:接入真实地理和军事数据