- 臧启航 的博客
Python3D我的世界(基本框架)
- @ 2025-5-26 21:45:49
警告,这只是一个基本框架
import pygame
import math
# 初始化Pygame
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
# 玩家参数
player_pos = [0.0, 1.6, 0.0] # (x, y, z)
player_rot = [0.0, 0.0] # 视角旋转
player_vel_y = 0.0 # 垂直速度
GRAVITY = -0.03 # 重力加速度
JUMP_FORCE = 0.28 # 跳跃力
PLAYER_HEIGHT = 1.8 # 玩家身高
PLAYER_WIDTH = 0.4 # 碰撞箱半径
ON_GROUND = False
FOV = 90
BLOCK_SIZE = 1
# 生成地形(地面+复杂墙体)
blocks = []
for x in range(-5, 6):
for z in range(-5, 6):
blocks.append((x, 0, z)) # 地面
if x == 3 and -2 <= z <= 2: # 多层墙体
blocks.append((x, 1, z))
blocks.append((x, 2, z))
blocks.append((x, 3, z))
blocks.append((2, 1, 3)) # 测试方块
blocks.append((-1, 1, -2)) # 空中平台
def block_exists(x, y, z):
"""精确方块存在检测"""
return (int(x), int(y), int(z)) in blocks
def check_collision(x, y, z):
"""三维碰撞箱检测"""
px_min = x - PLAYER_WIDTH
px_max = x + PLAYER_WIDTH
py_min = y
py_max = y + PLAYER_HEIGHT
pz_min = z - PLAYER_WIDTH
pz_max = z + PLAYER_WIDTH
for (bx, by, bz) in blocks:
# 方块碰撞箱范围
bx_min = bx - 0.5
bx_max = bx + 0.5
by_min = by - 0.5
by_max = by + 0.5
bz_min = bz - 0.5
bz_max = bz + 0.5
# 精确三维AABB检测
if (px_min < bx_max and px_max > bx_min and
py_min < by_max and py_max > by_min and
pz_min < bz_max and pz_max > bz_min):
return True
return False
def project_3d_to_2d(x, y, z):
"""3D透视投影(优化精度)"""
dx = x - player_pos[0]
dy = y - player_pos[1]
dz = z - player_pos[2]
# 水平旋转
angle_h = math.radians(player_rot[0])
x_rot = dx * math.cos(angle_h) - dz * math.sin(angle_h)
z_rot = dx * math.sin(angle_h) + dz * math.cos(angle_h)
# 垂直旋转
angle_v = math.radians(player_rot[1])
y_rot = dy * math.cos(angle_v) - z_rot * math.sin(angle_v)
z_final = z_rot * math.cos(angle_v) + dy * math.sin(angle_v)
if z_final <= 0:
return None
scale = FOV / z_final
return (
int(x_rot * scale + WIDTH//2),
int(-y_rot * scale + HEIGHT//2)
)
def draw_block(x, y, z):
"""绘制方块(优化线框)"""
vertices = []
for dx in [-0.5, 0.5]:
for dy in [-0.5, 0.5]:
for dz in [-0.5, 0.5]:
vert = project_3d_to_2d(
x + dx*BLOCK_SIZE,
y + dy*BLOCK_SIZE,
z + dz*BLOCK_SIZE
)
if vert:
vertices.append(vert)
if len(vertices) == 8:
# 只绘制可见边
for edge in [(0,1),(1,3),(3,2),(2,0),(4,5),(5,7),(7,6),(6,4),(0,4),(1,5),(3,7),(2,6)]:
pygame.draw.line(screen, (255,255,255), vertices[edge[0]], vertices[edge[1]])
def handle_input():
"""输入处理(防穿透优化)"""
global player_vel_y, ON_GROUND
keys = pygame.key.get_pressed()
move_speed = 0.1
angle = math.radians(player_rot[0])
# 分轴移动检测
original_x, original_z = player_pos[0], player_pos[2]
# X轴移动
new_x = original_x
if keys[pygame.K_w]:
new_x += math.sin(angle) * move_speed
if keys[pygame.K_s]:
new_x -= math.sin(angle) * move_speed
if keys[pygame.K_a]:
new_x -= math.cos(angle) * move_speed
if keys[pygame.K_d]:
new_x += math.cos(angle) * move_speed
# Z轴移动
new_z = original_z
if keys[pygame.K_w]:
new_z += math.cos(angle) * move_speed
if keys[pygame.K_s]:
new_z -= math.cos(angle) * move_speed
if keys[pygame.K_a]:
new_z += math.sin(angle) * move_speed
if keys[pygame.K_d]:
new_z -= math.sin(angle) * move_speed
# 分步碰撞检测
if not check_collision(new_x, player_pos[1], original_z):
player_pos[0] = new_x
if not check_collision(original_x, player_pos[1], new_z):
player_pos[2] = new_z
# 跳跃处理
if keys[pygame.K_SPACE] and ON_GROUND:
player_vel_y = JUMP_FORCE
ON_GROUND = False
# 鼠标控制
if pygame.mouse.get_focused():
dx, dy = pygame.mouse.get_rel()
player_rot[0] += dx * 0.1
player_rot[1] = max(-89, min(89, player_rot[1] - dy * 0.1))
def update_physics():
"""物理系统(防颤抖优化)"""
global player_pos, player_vel_y, ON_GROUND
# 应用重力
player_vel_y += GRAVITY
new_y = player_pos[1] + player_vel_y
# 三维地面检测
ON_GROUND = False
if player_vel_y < 0:
# 检测脚下5x5区域
for dx in [-0.4, -0.2, 0, 0.2, 0.4]:
for dz in [-0.4, -0.2, 0, 0.2, 0.4]:
if block_exists(player_pos[0]+dx, math.floor(new_y-0.1), player_pos[2]+dz):
new_y = math.floor(new_y-0.1) + 1.0
player_vel_y = 0
ON_GROUND = True
break
if ON_GROUND:
break
else:
# 头顶检测
if check_collision(player_pos[0], new_y + PLAYER_HEIGHT, player_pos[2]):
new_y = player_pos[1]
player_vel_y = 0
player_pos[1] = new_y
# 主循环
running = True
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
screen.fill((0, 0, 0))
handle_input()
update_physics()
# 按距离排序渲染(兼容旧Python版本)
def euclidean_distance(b):
dx = b[0] - player_pos[0]
dy = b[1] - player_pos[1]
dz = b[2] - player_pos[2]
return -math.sqrt(dx**2 + dy**2 + dz**2)
render_blocks = sorted(blocks, key=euclidean_distance)
for block in render_blocks:
draw_block(*block)
pygame.display.flip()
clock.tick(60)
pygame.mouse.set_visible(True)
pygame.event.set_grab(False)
pygame.quit()