如何在游戏中拥有本地位置和全球位置

时间:2009-06-25 17:31:29

标签: c# recursion 2d

我有一个名为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; }
}

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

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对象都是绘制为hud或gui对象而不是3D空间的对象。只是一个clareification。

更新

我正在考虑现在以递归方式进行,但我不确定如何做到这一点。

3 个答案:

答案 0 :(得分: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);
}

通过这种方式,您可以保留一个完全不关心屏幕的封装对象,以及一个处理所有翻译的Camera。

答案 1 :(得分:1)

如果您担心如何使用这些矩形,为什么要将它们暴露出来呢?您的组件可能只有自己的GetGlobalX, GetGlobalY, GetLocalX, GetLocalY,加上IsInside(X, Y)来检查鼠标悬停。然后它可以在内部使用矩形,也可以不使用矩形。

封装,哟。这不仅仅是早餐了。

答案 2 :(得分:1)

关于这些2D对象的内容,您的原始问题有点模糊,最佳答案取决于更严格的定义。

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

如果这些是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的根元素 - 这有利于根元素的屏幕空间和局部空间之间的转换,因为它们实际上是相同的。然后算法的其余部分可以在局部空间中工作。