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

Ursina Python:如何正确重设Entity的父对象?

问题描述

我用Ursina制作魔方,计划将某一面的所有方块设置为该面中心方块的子对象,通过旋转中心方块实现整面旋转。但仅首次设置父对象有效,后续无法成功修改方块的父对象。尝试过entity.parent = ___和entity.parent_setter(___)两种方式均未成功,相关代码如下:

from ursina import *

app = Ursina()

cubepiece = Entity(model='cube', color=color.black)
blueplate = Entity(model="cube", scale_x=0.9, scale_y=0.9, scale_z=0.1, z=-0.5, color=color.blue, parent=cubepiece)
yellowplate = Entity(model="cube", scale_x=0.1, scale_y=0.9, scale_z=0.9, x=-0.5, color=color.yellow, parent=cubepiece)
redplate = Entity(model="cube", scale_x=0.9, scale_y=0.1, scale_z=0.9, y=0.5, color=color.red, parent=cubepiece)
whiteplate = Entity(model="cube", scale_x=0.1, scale_y=0.9, scale_z=0.9, x=0.5, color=color.white, parent=cubepiece)
orangeplate = Entity(model="cube", scale_x=0.9, scale_y=0.1, scale_z=0.9, y=-0.5, color=color.orange, parent=cubepiece)
greenplate = Entity(model="cube", scale_x=0.9, scale_y=0.9, scale_z=0.1, z=0.5, color=color.green, parent=cubepiece)

cube = []
for i in range(3):
    layer = []
    for j in range(3):
        line = []
        for k in range(3):
            piece = duplicate(cubepiece)
            piece.set_position((i-1, j-1, k-1))
            line.append(piece)
        layer.append(line)
    cube.append(layer)

destroy(cubepiece)


for layer in cube:
    for line in layer:
        for piece in line:
            if piece != cube[1][1][1]:
                piece.parent = cube[1][1][1]

camera.x = 20
camera.y = 20
camera.fov = 30
camera.look_at((0, 0, 0))

def input(key):
    if key == 'w':
        rotateYellow()
    if key == "up arrow":
        cube[1][1][1].rotation_x += 45
    if key == "down arrow":
        cube[1][1][1].rotation_x -= 45
    if key == "left arrow":
        cube[1][1][1].rotation_y += 45
    if key == "right arrow":
        cube[1][1][1].rotation_y -= 45

def rotateYellow():
    for line in cube[0]:
        for piece in line:
            if piece != cube[0][1][1]:
                piece.parent_setter(cube[0][1][1])
    cube[0][1][1].rotation_x += 45
    for line in cube[0]:
        for piece in line:
            piece.parent_setter(cube[1][1][1])

app.run()

问题根源

  1. 父对象切换未保留世界坐标:直接修改父对象会让子对象继承父对象的局部空间,导致方块位置偏移,视觉上看起来切换失败。
  2. 错误使用parent_setter:Ursina中设置父对象的正确方式是直接赋值entity.parent = 目标对象,parent_setter是内部方法,不应直接调用。
  3. 旋转后未更新方块索引:魔方旋转后,方块在cube数组中的索引与实际空间位置不再匹配,后续操作会指向错误的方块。

修正后的代码

from ursina import *

app = Ursina()

# 定义带六个色块的魔方块
cubepiece = Entity(model='cube', color=color.black)
blueplate = Entity(model="cube", scale=(0.9,0.9,0.1), z=-0.5, color=color.blue, parent=cubepiece)
yellowplate = Entity(model="cube", scale=(0.1,0.9,0.9), x=-0.5, color=color.yellow, parent=cubepiece)
redplate = Entity(model="cube", scale=(0.9,0.1,0.9), y=0.5, color=color.red, parent=cubepiece)
whiteplate = Entity(model="cube", scale=(0.1,0.9,0.9), x=0.5, color=color.white, parent=cubepiece)
orangeplate = Entity(model="cube", scale=(0.9,0.1,0.9), y=-0.5, color=color.orange, parent=cubepiece)
greenplate = Entity(model="cube", scale=(0.9,0.9,0.1), z=0.5, color=color.green, parent=cubepiece)

# 生成3x3x3魔方数组,给每个方块标记索引
cube = []
for i in range(3):
    layer = []
    for j in range(3):
        line = []
        for k in range(3):
            piece = duplicate(cubepiece)
            piece.set_position((i-1, j-1, k-1))
            piece.index = (i,j,k)  # 记录初始索引
            line.append(piece)
        layer.append(line)
    cube.append(layer)

destroy(cubepiece)

center_piece = cube[1][1][1]
# 初始将所有非中心块设为中心块子对象
for layer in cube:
    for line in layer:
        for piece in line:
            if piece != center_piece:
                piece.parent = center_piece

camera.position = (20,20,20)
camera.fov = 30
camera.look_at((0,0,0))

def update_cube_indices(layer_idx):
    """旋转后更新cube数组的方块索引映射"""
    layer = cube[layer_idx]
    new_layer = [[None]*3 for _ in range(3)]
    for j in range(3):
        for k in range(3):
            piece = layer[j][k]
            # 通过世界坐标计算新索引
            world_pos = piece.world_position
            x = round(world_pos.x) + 1
            y = round(world_pos.y) + 1
            z = round(world_pos.z) + 1
            new_layer[y][z] = piece
            piece.index = (x,y,z)
    cube[layer_idx] = new_layer

def rotateYellow():
    yellow_center = cube[0][1][1]
    # 切换父对象前保存世界坐标,避免位置偏移
    for line in cube[0]:
        for piece in line:
            if piece != yellow_center:
                world_pos = piece.world_position
                piece.parent = yellow_center
                piece.world_position = world_pos

    # 平滑旋转并在完成后重置父对象、更新索引
    def on_rotation_finish():
        for line in cube[0]:
            for piece in line:
                if piece != yellow_center:
                    world_pos = piece.world_position
                    piece.parent = center_piece
                    piece.world_position = world_pos
        update_cube_indices(0)

    yellow_center.animate('rotation_x', yellow_center.rotation_x + 90, duration=0.2, curve=curve.linear, callback=on_rotation_finish)

def input(key):
    if key == 'w':
        rotateYellow()
    if key == "up arrow":
        center_piece.rotation_x += 45
    if key == "down arrow":
        center_piece.rotation_x -= 45
    if key == "left arrow":
        center_piece.rotation_y += 45
    if key == "right arrow":
        center_piece.rotation_y -= 45

app.run()

关键修正说明

  • 保留世界坐标:切换父对象前后,先保存world_position,设置父对象后再赋值回去,确保方块位置不变。
  • 正确设置父对象:使用piece.parent = 目标对象替代内部方法parent_setter。
  • 旋转后更新索引:魔方旋转完成后,根据方块世界坐标重新映射数组索引,保证后续操作能选中正确的方块。
  • 平滑旋转回调:用animate实现平滑旋转,旋转完成后再重置父对象,避免旋转过程中出现坐标混乱。

内容的提问来源于stack exchange,提问作者Iby Morris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:30:54