Как иметь локальную позицию и глобальную позицию в игре - PullRequest
1 голос
/ 25 июня 2009

У меня есть базовый класс под названием Component. У меня также есть 2 интерфейса I2DComponent и I3DComponent. В настоящее время я работаю над компонентом I2DC.

Реализация:

/// <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 имел только Границы и масштаб, а затем имел функцию в GameScreen для получения текущей «Глобальной» позиции компонента.

Обновление:

Все эти 2D-объекты - это объекты, которые нарисованы как объекты hud или gui, а не в трехмерном пространстве. просто уточнение.

Обновление:

Сейчас я думаю сделать это рекурсивно, но я не уверен, как бы я поступил так.

Ответы [ 3 ]

3 голосов
/ 25 июня 2009

Я бы только беспокоился об изменении локального положения вашего игрового объекта, а не его положения на экране, потому что это зависит от расположения и порта просмотра вашей камеры.

Есть два способа найти положение чего-либо на экране. Первый - это метод доступа, который использует камеру, а второй - использование камеры в качестве метода перевода. Я предпочитаю позже, и переписать таким образом:

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);
}

Таким образом, вы сохраняете инкапсулированный объект, который не заботится об экране, и камеру, которая обрабатывает весь перевод.

1 голос
/ 25 июня 2009

Ваш первоначальный вопрос относительно этих двухмерных объектов немного расплывчат, и лучший ответ зависит от более строгого определения.

Если это объекты в вашем игровом мире (например, имена над головами, рекламные щиты), то им нужна позиция в мире, и вы переводите ее в положение на экране, как правило, с помощью класса камеры, как описано в другом ответе.

Если, однако, это объекты графического интерфейса пользователя, такие как меню, формы, заголовки и т. Д., То это совсем другое. В этом случае вам нужно только сохранить позицию относительно родительского элемента (т.е. локальную позицию), и рисование их может быть выполнено рекурсивно. Положение каждого элемента - это, в основном, глобальная позиция его родителя плюс его собственная локальная позиция, и это поднимается вверх по дереву любым родителям верхнего уровня, локальные позиции которых также являются глобальными позициями.

РЕДАКТИРОВАТЬ: пример, приведенный для пояснения. Неопробованный псевдокод в 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)

Вам могут потребоваться две отдельные функции: одна, которая получает точный целевой элемент (как указано выше), и другая, которая просто возвращает, если мышь находится где-либо над элементом, которая может возвращать 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 - это облегчает преобразование между экранным пространством и локальным пространством для корневого элемента, потому что они фактически одинаковы. Тогда остальная часть алгоритма может работать в локальном пространстве.

1 голос
/ 25 июня 2009

Если вы беспокоитесь о том, как будут использоваться эти прямоугольники, зачем вообще их выставлять? Ваш компонент может иметь свой собственный GetGlobalX, GetGlobalY, GetLocalX, GetLocalY, плюс, возможно, IsInside(X, Y) для проверки наведения мыши. Тогда он может использовать прямоугольники внутри себя или нет, как ему нравится.

Инкапсуляция, йо. Это больше не только на завтрак.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...