Python + Ursina设计一个有趣的3D小游戏
Ursina使用基础知识可见 https://blog.csdn.net/cnds123/article/details/155709276
Python + Ursina设计一个有趣的3D小游戏:《天空跑酷:寻找金立方》
项目文件夹结构是这样的:
你的文件夹/
├── main.py (下面的代码)
├── SimHei.ttf (必须有!用于显示中文)
└── sounds/ (文件夹)
├── jump.wav (跳跃音效,可选)
└── win.wav (胜利音效,可选)
运行截图:

完整代码如下:
python
## 玩法说明
## 脚踩 灰色 起点(出生地)。
## 一路跳过 绿色 方块(稳扎稳打)。绿色块是安全踏板。
## 小心应对 蓝色 方块(看准时机)。左右摆动的蓝色块是给游戏增加难度的"拦路虎"。
## 绝对不要碰 红色 地面(碰了就重来)。红色区域是万丈深渊的底部。
## 最终摸到 黄色 小方块(通关胜利)。
## 鼠标移动:转动视角(看天、看地、看路)。注意:进入游戏后鼠标光标会消失,这是正常的 FPS 模式。
## 在所有绿色平台的尽头,悬浮着一个金黄色的大方块(还会旋转)。
## W 键:向前走。S 键:向后退。A 键:向左移。D 键:向右移。空格键 (Space):跳跃。
## 按住W冲刺,看准时机按空格,飞向下一个绿色方块;有的平台离得远,需要按住 W 助跑再按空格;有的离得近,原地起跳就行。
## ESC 键:直接强制退出游戏。
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
import random
import os # os._exit(0) 用到
# 初始化引擎
app = Ursina()
# --- 1. 设置中文字体 (必须!) ---
Text.default_font = 'SimHei.ttf'
# --- 2. 音频设置 (运用你刚学的知识) ---
# 即使文件不存在,Ursina 通常只是报警告而不会崩溃,但建议你放进去体验更好
jump_sound_path = 'sounds/jump.wav' # 显式路径写法,稳定可靠
win_sound_path = 'sounds/win.wav'
def play_audio_if_exists(path):
# 一个安全播放音频的小函数
if os.path.exists(path):
Audio(path, autoplay=True)
# --- 3. 场景搭建 ---
# 天空盒 (给游戏一个背景)
Sky()
# 地面 (Death Zone) - 用红色表示危险,掉在上面会重置
death_zone = Entity(
model='plane',
scale=(100, 1, 100),
color=color.red,
y=-10,
collider='box',
texture='white_cube' # 内置纹理
)
# 起点平台
start_platform = Entity(
model='cube',
color=color.gray,
scale=(5, 1, 5),
position=(0, 0, 0),
collider='box',
texture='white_cube'
)
# --- 4. 关卡生成算法 ---
# 我们运用左手坐标系:Y是高,Z是前
platforms = []
z_pos = 0
y_pos = 0
# 生成 15 个随机跳跃平台
for i in range(15):
# 每次往前(Z)走 4~6米,往上(Y)走 -1~2米 (可能高也可能低)
z_pos += random.uniform(4, 7) # Z-Forward(向前是Z+)。降低难度修改:改成 3~5:z_pos += random.uniform(3, 5),缩小平台之间的缝隙
y_pos += random.uniform(-1, 2) # Y-Up(向上是Y+)
# 随机左右偏移(X)
x_pos = random.uniform(-3, 3)
# 创建平台
p = Entity(
model='cube',
color=color.green,
scale=(3, 0.5, 3), # 稍微扁一点。降低难度修改:改成 5或者更大 scale=(5, 0.5, 5)
position=(x_pos, y_pos, z_pos),
collider='box',
texture='brick'
)
platforms.append(p)
# 给第 5 个和第 10 个平台加特效:它们是会移动的!
if i == 5 or i == 10:
p.color = color.azure
p.is_moving = True # 自定义属性标记
else:
p.is_moving = False
# --- 5. 终点目标 ---
goal_block = Entity(
model='cube',
color=color.gold,
scale=(2, 2, 2),
position=(0, y_pos + 1, z_pos + 6), # 在最后一个平台前面一点
collider='box',
texture='white_cube'
)
# --- 6. 玩家设置 ---
player = FirstPersonController(
model='cube',
collider='box',
gravity=0.8 # 重力参数。降低难度修改:降低到 0.5 或 0.4
)
player.cursor.visible = False # 隐藏鼠标
# --- 7. UI 文本 ---
msg_text = Text(
text='按 WASD 移动,空格跳跃\n目标:找到金色方块!',
origin=(0, 0),
y=0.3,
scale=2,
color=color.yellow
)
# 胜利状态标记
game_won = False
# --- 8. 游戏主逻辑 (Update) ---
def update():
global game_won
if game_won:
return # 如果赢了,就不运行后面的逻辑了
# 1. 检测是否掉落 (Y轴判定)
if player.y < -5:
player.position = (0, 1, 0) # 回到起点
msg_text.text = '你掉下去了!\n已重置位置'
msg_text.color = color.red
invoke(reset_text, delay=2) # 2秒后重置文字
# 2. 检测移动平台逻辑
# time.time() 返回当前时间,sin 函数让数值在 -1 到 1 之间波动
for p in platforms:
if hasattr(p, 'is_moving') and p.is_moving:
# 让平台在 X 轴方向左右摇摆
p.x += math.sin(time.time() * 2) * 0.05 # 产生平滑左右摇摆的平台。降低难度修改:* 0.05 改成 * 0.01
# 3. 检测胜利 (检测玩家和金块的距离)
# distance(a, b) 是 Ursina 内置函数
if distance(player.position, goal_block.position) < 2:
win_game()
# --- 9. 输入控制 (Input) ---
def input(key):
# 【调试大法】打印当前按下的键
print(f"检测到按键: {key}")
if key == 'space':
# 简单的跳跃音效逻辑
# 只有当玩家在地上时才播放声音(Userina的player.grounded属性)
if player.grounded:
play_audio_if_exists(jump_sound_path)
# 用 ESC 退出
if key == 'escape':
print("按下了 ESC")
#application.quit() # 不用------不起效,它太温柔了
os._exit(0) # 重点!
# 辅助函数
def reset_text():
if not game_won:
msg_text.text = '加油!往 Z 轴正方向前进!'
msg_text.color = color.white
def win_game():
global game_won
game_won = True
msg_text.text = '恭喜!挑战成功!\n按 ESC 退出'
msg_text.color = color.green
msg_text.scale = 3
msg_text.background = True
play_audio_if_exists(win_sound_path)
# 一个小彩蛋:让金块旋转起来
goal_block.animate_rotation_y(360, duration=1, loop=True)
app.run()
说明
**1.**在代码的input(key)函数里只写了Space播放声音,没写if key == 'w': move_forward(),为什么它自己就会走呢?
因为使用了一个"现成的超级零件" ------FirstPersonController。这个零件(类)是 Ursina 官方帮你写好的,它内部封装了复杂的数学运算和物理逻辑。
from ursina.prefabs.first_person_controller import FirstPersonController 这行代码导入了 Ursina 的一个预制的第一人称控制器,它是一个已经封装好的、功能完整的角色控制器。也可以说,FirstPersonController是 Ursina 引擎中用于快速创建第一人称视角玩家控制器的预制组件(prefab)。FirstPersonController的作用包含以下功能:
• 鼠标控制视角(左右/上下看)
• WASD 移动
• 空格键跳跃
• 重力与地面碰撞检测
• 自动站在地面上(不会掉下去)
• 可调节速度、跳跃高度、视角灵敏度等参数
简单示例:
python
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
# 设置窗口大小(宽, 高)
window.size = (1280, 720)
app = Ursina()
# 创建地面
ground = Entity(model='plane', scale=10, color=color.green, collider='box')
# 创建第一人称玩家
player = FirstPersonController()
app.run()
现在你可以试试这个简单的示例是否正常运行:
• 移动鼠标:视角应该会转动(即使窗口小,也能看到地面颜色变化)。
• 按 WASD:角色会移动(虽然看不到自己,但相机位置在变)。
• 按空格:会跳起(可配合向下看观察地面距离变化)。
如果想用箭头键取代WASD 键控制角色移动怎么办?
在内置函数update()中使用held_keys字典映射是最简便的方式
update()是Ursina 引擎中内置函数,一个每帧自动调用的函数,用于实现持续更新的游戏逻辑,比如角色移动、动画、物理、AI 行为等。
示例代码:
python
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
# 设置窗口大小(宽, 高)
window.size = (1280, 720)
app = Ursina()
# 创建地面
ground = Entity(model='plane', scale=10, color=color.green, collider='box')
# 创建第一人称玩家
player = FirstPersonController()
# 将方向键映射到 WASD
def update():
held_keys['w'] = held_keys['up arrow']
held_keys['s'] = held_keys['down arrow']
held_keys['a'] = held_keys['left arrow']
held_keys['d'] = held_keys['right arrow']
app.run()
你可以运行试试看。
2.如何调整游戏的难度?
可以通过调整几个关键参数,如想降低游戏的难度:
①调整引力
原代码
gravity=0.8
修改为:降低到 0.5 或 0.4
gravity=0.5
②踏板变大 (增大落脚面积)
原代码 (3米宽)
scale=(3, 0.5, 3)
修改为:改成 5米或者更大
scale=(5, 0.5, 5)
③拉近平台距离 (减少跳跃跨度)
原代码 (间距 4~7米)
z_pos += random.uniform(4, 7)
修改为:改成 3~5米
z_pos += random.uniform(3, 5)
④降低两个捣乱的蓝色方块移动速度
原代码 (0.05 是移动速度幅度)
p.x += math.sin(time.time() * 2) * 0.05
修改为:改成 0.01,几乎不动了
p.x += math.sin(time.time() * 2) * 0.01
你可以修改试试。