2

我有一个名为 Component 的基类。我也有 2 个接口 I2DComponent 和 I3DComponent。我目前正在研究 I2DComponent。

实施:

/// <summary>
/// Represtions a 2D object. These objects will be drawn after 3D objects,
/// and will have modifiers automatically provided in the editor.
/// </summary>
public interface I2DComponent
{
    /// <summary>
    /// Gets or sets the rectangle.
    /// </summary>
    /// <value>The rectangle.</value>
    Rectangle Rectangle { get; set; }

    /// <summary>
    /// Gets the local position on the game screen.
    /// </summary>
    /// <value>The local position on the game screen.</value>
    /// <remarks>The rectangle position minus the parent screen position.</remarks>
    Rectangle LocalPosition { get; }

    /// <summary>
    /// Gets or sets the scale.
    /// </summary>
    /// <value>The scale.</value>
    float Scale { get; set; }
}

所以问题是我需要访问全局位置或矩形来检查鼠标是否在它上面。

if (component.Rectangle.Contains(mouse.Rectangle))
{
    do somthing.
}

但是当我只是访问移动它时,我只想访问本地位置,这样如果我在位置 50、50 有一个游戏屏幕,那么我可以将本地位置设置为 0、0,它将位于顶部父游戏屏幕的左上角。我想真正的问题是我不想通过说

component.Rectangle = new Rectangle(component.Rectangle.X + 5, component.Rectangle.Y + 5, component.Rectangle.Width, component.Rectangle.Height);

这或多或少是一个访问器设计问题,我想让它看起来正确,但我在这样做时遇到了麻烦。

更新:

如果我改变它,使 I2D 组件只有 Bounds 和 scale,然后在 GameScreen 中有一个函数来获取组件的当前“全局”位置,该怎么办。

更新:

这些 2D 对象都是在 3D 空间中绘制为 hud 或 gui 对象的所有对象。只是澄清一下。

更新:

我现在正在考虑递归地做它,但我不确定我将如何去做。

4

3 回答 3

3

我只会担心修改游戏对象的本地位置,而不是屏幕位置,因为这取决于相机的位置和视口。

有两种方法可以解决在屏幕上查找某物的位置。第一种是使用获取相机的访问器方法,另一种是首先使用相机作为翻译方法。我更喜欢后者,因此重写:

public interface I2DComponent
{
    /// <summary>
    /// Gets or sets the local (world) bounds of the object.
    /// </summary>
    /// <value>The rectangle.</value>
    Rectangle Bounds{ get; }

    /// <summary>
    /// Gets or sets the scale.
    /// </summary>
    /// <value>The scale.</value>
    float Scale { get; set; }
}

然后在相机中有两种方法

public class Camera
{
    public Rectangle WorldToScreen(Rectangle rect);
    public Rectangle ScreenToWorld(Rectangle point);
}

这样你就可以保留一个完全不关心屏幕的封装对象,以及一个处理所有翻译的相机。

于 2009-06-25T17:44:39.867 回答
1

如果您担心这些 Rectangle 将如何被使用,为什么要公开它们呢?您的组件可能只有它自己的GetGlobalX, GetGlobalY, GetLocalX, GetLocalY,可能还有一个IsInside(X, Y)用于检查鼠标悬停的。然后它可以根据需要在内部使用或不使用 Rectangles。

封装,哟。它不再只是早餐。

于 2009-06-25T17:39:28.170 回答
1

您最初的问题对于这些 2D 对象是什么有点模糊,最佳答案取决于更严格的定义。

如果这些是您游戏世界中的对象(例如,头顶上的名称、广告牌),那么它们需要一个世界位置,并且您通常通过相机类将其转换为屏幕位置,如另一个答案中所述。

然而,如果这些是 GUI 对象,例如菜单、表单、平视显示器等,那就完全不同了。在这种情况下,您只需要存储相对于父元素的位置(即本地位置),并且可以递归地绘制它们。每个元素的位置基本上是它的父元素的全局位置加上它自己的局部位置,然后向上到任何顶级父元素,其局部位置也是全局位置。

编辑:给出澄清的例子。Python 风格的未经测试的伪代码(抱歉,因为我不懂 C#)

def draw(element, parentScreenX, parentScreenY):
    # Draw this element relative to its parent
    screenX = parentScreenX + element.localX
    screenY = parentScreenY + element.localY    
    render(element, screenX, screenY)
    # Draw each child relative to this one
    for childElement in element.children:
        draw(childElement, screenX, screenY)

# Normal usage (root element should be at 0,0, 
# and typically just an invisible container.)
draw(rootElement, 0, 0)


# Slightly different mathematics for the mouseover test,
# but that's just personal preference
def elementAtPos(element, posX, posY):
    # Translate that space to element local space
    posX -= element.localX
    posY -= element.localY
    # Compare against this element's dimensions
    if (posX, posY) is within (0, 0, element.width, element.height):
        # it's within this element - now check children
        for childElement in element.children:
            # Do this same check on the child element (and its children, etc)
            targetedChildElement = elementAtPos(childElement, posX, posY) 
            if targetedChildElement is not None:
                return targetedChildElement
        # it wasn't within any of our descendants so return this one
        return element 
    # It wasn't within our bounds at all, so return None
    return None

#Normal usage
targetedElement = elementAtPos(rootElement, mouseScreenX, mouseScreenY)

您可能需要 2 个单独的函数:一个获取确切的目标元素(如上),另一个仅在鼠标位于元素上方时返回,它可能同时对多个元素返回 true,例如。一个按钮,按钮所在的面板,面板所在的窗体等。有几种方法可以实现,但可能最简单的方法是实现递归调用以允许任何元素获取其屏幕位置:

def screen_position(element):
    if element.parent is not None:
        (parentLocalX, parentLocalY) = screen_position(element.parent)
    else:
        (parentLocalX, parentLocalY) = (0,0)
    return (element.localX + parentLocalX, element.localY + parentLocalY)

现在注意父母和孩子之间的循环引用。

这些都需要一个位于 0,0 的根元素——这有助于根元素的屏幕空间和本地空间之间的转换,因为它们实际上是相同的。然后算法的其余部分可以在局部空间中工作。

于 2009-06-25T19:54:03.533 回答