如何在Ursina Python中优化我的世界克隆版并解决方块放置异常
Ursina我的世界克隆版方块放置问题修复
问题描述
使用Ursina开发我的世界克隆版时,初始状态下玩家5米范围内可正常放置方块;但玩家移动后,无法在5米半径外、10米范围内放置方块。尝试将「移除远距离实体碰撞体」的逻辑复制到玩家移动触发的函数中,问题仍未解决。
问题根源
- Block实例事件响应局限性:原代码中每个Block实例仅在用户按下WASD按键时才更新自身的碰撞体和可见性,但并非所有Block都会触发该事件,导致远距离Block的碰撞体始终为
None,无法被放置方块的射线检测(raycast)命中。 - 碰撞体与射线检测范围不匹配:射线检测的最大距离设为10米,但碰撞体仅在5米内才启用,超出5米的Block无碰撞体,射线检测无法识别这些实体,自然无法在其表面放置新方块。
修复方案
将Block的状态更新逻辑从单个实例的input方法移至全局update函数,实时遍历所有Block实例,根据玩家位置动态调整碰撞体和可见性,确保射线检测范围内的Block都有碰撞体:
代码修改步骤
- 移除Block类中的input方法:删除原Block类内的
input函数,避免局部且低效的状态更新。 - 重构Block初始化方法:初始化时暂不设置碰撞体和可见性,留待全局update统一处理。
- 全局update函数中批量更新Block状态:遍历场景中所有Block实例,计算与玩家的距离,将碰撞体启用范围调整为10米(与射线检测距离一致),同时控制可见性。
修改后的完整代码
import math from ursina import * from ursina.prefabs.first_person_controller import FirstPersonController from perlin_noise import PerlinNoise import random from distance_calc import distance_calc noise = PerlinNoise(octaves=5, seed=random.randint(-1000000000000, 10000000000)) optimal_distance = 5 # 仅用于参考,实际碰撞体范围改为10米 app = Ursina() # 变量 selected_block = "grass" # 纹理 block_textures = { 'grass': load_texture("assets/grass.jpg"), 'dirt': load_texture("assets/dirt.jpg"), 'stone': load_texture("assets/stone.jpg"), 'bedrock': load_texture("assets/bedrock.jpg"), 'faux_bedrock': load_texture("assets/bedrock.jpg"), 'cobblestone': load_texture("assets/cobblestone.jpg") } # 创建玩家 player = FirstPersonController( mouse_sensitivity=Vec2(100, 100), scale=0.85, position=(0, 25, 0) ) class Block(Entity): def __init__(self, position, block_type): super().__init__( position=position, model="cube", scale=1, origin_y=-0.5, texture=block_textures.get(block_type), ) self.block_type = block_type # 初始化时不设置状态,由全局update统一更新 self.collider = None self.visible = False mini_block = Entity( parent=camera, model="cube", scale=0.2, origin_y=-0.5, texture=block_textures.get(selected_block), collider="box", position=(0.3, -0.25, 0.45), ) # 地面(水) ground = Entity( model='plane', scale=(100, 1, 100), texture="water", texture_scale=(100, 100), collider="box", block_type="water", position=(0,-5, 0), ) # 放置/破坏方块 def input(key): global selected_block # 放置方块 if key == "right mouse down": hit_info = raycast(camera.world_position, camera.forward, distance=10) if hit_info.hit: block = Block(hit_info.entity.position + hit_info.normal, selected_block) # 破坏方块 if key == "left mouse down" and mouse.hovered_entity: if mouse.hovered_entity.block_type not in ["bedrock", "water"]: destroy(mouse.hovered_entity) # 切换选中方块 if key=="1": selected_block = "grass" if key=="2": selected_block = "stone" if key=="3": selected_block = "faux_bedrock" if key=="4": selected_block = "cobblestone" # 生成地形 min_height = -2 for x in range (-25, 25): for z in range(-25, 25): height = noise([x*0.02, z*0.02]) height = math.floor(height * 7.5) for y in range(height, min_height -1, -1): if y == min_height: block = Block((x,y + min_height, z), "bedrock") elif y == height: block = Block((x,y + min_height, z), "grass") elif height - y > 2: block = Block((x,y + min_height, z), "stone") else: block = Block((x,y + min_height, z), "dirt") def update(): mini_block.texture = block_textures.get(selected_block) # 实时更新所有Block的碰撞体和可见性 for entity in scene.entities: if isinstance(entity, Block): distance = distance_calc(player.x, player.y, player.z, entity.x, entity.y, entity.z) # 碰撞体启用范围与射线检测距离一致(10米),确保放置检测能命中 entity.collider = "box" if distance < 10 else None entity.visible = distance < 10 # 运行应用 app.run()
效果验证
修改后,玩家移动时所有10米内的Block会自动启用碰撞体并保持可见,射线检测可以正常命中这些实体,因此能在5米外、10米范围内正常放置方块,同时10米外的Block会关闭碰撞体并隐藏,达到优化性能的目的。
内容的提问来源于stack exchange,提问作者Coding Master
相关产品推荐
相关产品推荐

