You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ursina Python中优化我的世界克隆版并解决方块放置异常

Ursina我的世界克隆版方块放置问题修复

问题描述

使用Ursina开发我的世界克隆版时,初始状态下玩家5米范围内可正常放置方块;但玩家移动后,无法在5米半径外、10米范围内放置方块。尝试将「移除远距离实体碰撞体」的逻辑复制到玩家移动触发的函数中,问题仍未解决。

问题根源

  1. Block实例事件响应局限性:原代码中每个Block实例仅在用户按下WASD按键时才更新自身的碰撞体和可见性,但并非所有Block都会触发该事件,导致远距离Block的碰撞体始终为None,无法被放置方块的射线检测(raycast)命中。
  2. 碰撞体与射线检测范围不匹配:射线检测的最大距离设为10米,但碰撞体仅在5米内才启用,超出5米的Block无碰撞体,射线检测无法识别这些实体,自然无法在其表面放置新方块。

修复方案

将Block的状态更新逻辑从单个实例的input方法移至全局update函数,实时遍历所有Block实例,根据玩家位置动态调整碰撞体和可见性,确保射线检测范围内的Block都有碰撞体:

代码修改步骤

  1. 移除Block类中的input方法:删除原Block类内的input函数,避免局部且低效的状态更新。
  2. 重构Block初始化方法:初始化时暂不设置碰撞体和可见性,留待全局update统一处理。
  3. 全局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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:17:36