/** GDevelop - Anchor Behavior Extension Copyright (c) 2016 Victor Levasseur (victorlevasseur52@gmail.com) This project is released under the MIT License. */ #include "AnchorRuntimeBehavior.h" #include #include #include #include #include #include #include "GDCore/CommonTools.h" #include "GDCore/Tools/Localization.h" #include "GDCpp/Extensions/Builtin/MathematicalTools.h" #include "GDCpp/Runtime/CommonTools.h" #include "GDCpp/Runtime/Project/Layout.h" #include "GDCpp/Runtime/RuntimeGame.h" #include "GDCpp/Runtime/RuntimeObject.h" #include "GDCpp/Runtime/RuntimeScene.h" #include "GDCpp/Runtime/Serialization/SerializerElement.h" #if defined(GD_IDE_ONLY) #include #include "GDCore/Project/PropertyDescriptor.h" #endif AnchorRuntimeBehavior::AnchorRuntimeBehavior( const gd::SerializerElement& behaviorContent) : RuntimeBehavior(behaviorContent), m_relativeToOriginalWindowSize(true), m_leftEdgeAnchor(ANCHOR_HORIZONTAL_NONE), m_rightEdgeAnchor(ANCHOR_HORIZONTAL_NONE), m_topEdgeAnchor(ANCHOR_VERTICAL_NONE), m_bottomEdgeAnchor(ANCHOR_VERTICAL_NONE), m_invalidDistances(true), m_leftEdgeDistance(0.f), m_rightEdgeDistance(0.f), m_topEdgeDistance(0.f), m_bottomEdgeDistance(0.f) { m_relativeToOriginalWindowSize = behaviorContent.GetBoolAttribute("relativeToOriginalWindowSize"); m_leftEdgeAnchor = static_cast( behaviorContent.GetIntAttribute("leftEdgeAnchor")); m_rightEdgeAnchor = static_cast( behaviorContent.GetIntAttribute("rightEdgeAnchor")); m_topEdgeAnchor = static_cast( behaviorContent.GetIntAttribute("topEdgeAnchor")); m_bottomEdgeAnchor = static_cast( behaviorContent.GetIntAttribute("bottomEdgeAnchor")); } void AnchorRuntimeBehavior::OnActivate() { m_invalidDistances = true; } namespace { sf::Vector2f mapFloatPixelToCoords(const sf::Vector2f& point, const sf::RenderTarget& target, const sf::View& view) { // First, convert from viewport coordinates to homogeneous coordinates sf::Vector2f normalized; sf::IntRect viewport = target.getViewport(view); normalized.x = -1.f + 2.f * (point.x - static_cast(viewport.left)) / static_cast(viewport.width); normalized.y = 1.f - 2.f * (point.y - static_cast(viewport.top)) / static_cast(viewport.height); // Then transform by the inverse of the view matrix return view.getInverseTransform().transformPoint(normalized); } sf::Vector2f mapCoordsToFloatPixel(const sf::Vector2f& point, const sf::RenderTarget& target, const sf::View& view) { // Note: almost the same as RenderTarget::mapCoordsToPixel except that the // result is sf::Vector2f // First, transform the point by the view matrix sf::Vector2f normalized = view.getTransform().transformPoint(point); // Then convert to viewport coordinates sf::Vector2f pixel; sf::IntRect viewport = target.getViewport(view); pixel.x = (normalized.x + 1.f) / 2.f * static_cast(viewport.width) + static_cast(viewport.left); pixel.y = (-normalized.y + 1.f) / 2.f * static_cast(viewport.height) + static_cast(viewport.top); return pixel; } } // namespace void AnchorRuntimeBehavior::DoStepPreEvents(RuntimeScene& scene) {} void AnchorRuntimeBehavior::DoStepPostEvents(RuntimeScene& scene) { const RuntimeLayer& layer = scene.GetRuntimeLayer(object->GetLayer()); const RuntimeCamera& firstCamera = layer.GetCamera(0); if (m_invalidDistances) { sf::Vector2u windowSize = m_relativeToOriginalWindowSize ? sf::Vector2u(scene.game->getWindowOriginalWidth(), scene.game->getWindowOriginalHeight()) : scene.renderWindow->getSize(); // Calculate the distances from the window's bounds. sf::Vector2f topLeftPixel = mapCoordsToFloatPixel( sf::Vector2f(object->GetDrawableX(), object->GetDrawableY()), *(scene.renderWindow), firstCamera.GetSFMLView()); sf::Vector2f bottomRightPixel = mapCoordsToFloatPixel( sf::Vector2f(object->GetDrawableX() + object->GetWidth(), object->GetDrawableY() + object->GetHeight()), *(scene.renderWindow), firstCamera.GetSFMLView()); // Left edge if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_LEFT) m_leftEdgeDistance = topLeftPixel.x; else if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_RIGHT) m_leftEdgeDistance = static_cast(windowSize.x) - topLeftPixel.x; else if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_PROPORTIONAL) m_leftEdgeDistance = topLeftPixel.x / windowSize.x; // Right edge if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_LEFT) m_rightEdgeDistance = bottomRightPixel.x; else if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_RIGHT) m_rightEdgeDistance = static_cast(windowSize.x) - bottomRightPixel.x; else if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_PROPORTIONAL) m_rightEdgeDistance = bottomRightPixel.x / windowSize.x; // Top edge if (m_topEdgeAnchor == ANCHOR_VERTICAL_WINDOW_TOP) m_topEdgeDistance = topLeftPixel.y; else if (m_topEdgeAnchor == ANCHOR_VERTICAL_WINDOW_BOTTOM) m_topEdgeDistance = static_cast(windowSize.y) - topLeftPixel.y; else if (m_topEdgeAnchor == ANCHOR_VERTICAL_PROPORTIONAL) m_topEdgeDistance = topLeftPixel.y / static_cast(windowSize.y); // Bottom edge if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_WINDOW_TOP) m_bottomEdgeDistance = bottomRightPixel.y; else if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_WINDOW_BOTTOM) m_bottomEdgeDistance = static_cast(windowSize.y) - bottomRightPixel.y; else if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_PROPORTIONAL) m_bottomEdgeDistance = bottomRightPixel.y / static_cast(windowSize.y); m_invalidDistances = false; } else { sf::Vector2u windowSize = scene.renderWindow->getSize(); // Move and resize the object if needed sf::Vector2f topLeftPixel; sf::Vector2f bottomRightPixel; // Left edge if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_LEFT) topLeftPixel.x = m_leftEdgeDistance; else if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_RIGHT) topLeftPixel.x = static_cast(windowSize.x) - m_leftEdgeDistance; else if (m_leftEdgeAnchor == ANCHOR_HORIZONTAL_PROPORTIONAL) topLeftPixel.x = m_leftEdgeDistance * static_cast(windowSize.x); // Top edge if (m_topEdgeAnchor == ANCHOR_VERTICAL_WINDOW_TOP) topLeftPixel.y = m_topEdgeDistance; else if (m_topEdgeAnchor == ANCHOR_VERTICAL_WINDOW_BOTTOM) topLeftPixel.y = static_cast(windowSize.y) - m_topEdgeDistance; else if (m_topEdgeAnchor == ANCHOR_VERTICAL_PROPORTIONAL) topLeftPixel.y = m_topEdgeDistance * static_cast(windowSize.y); // Right edge if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_LEFT) bottomRightPixel.x = m_rightEdgeDistance; else if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_WINDOW_RIGHT) bottomRightPixel.x = static_cast(windowSize.x) - m_rightEdgeDistance; else if (m_rightEdgeAnchor == ANCHOR_HORIZONTAL_PROPORTIONAL) bottomRightPixel.x = m_rightEdgeDistance * static_cast(windowSize.x); // Bottom edge if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_WINDOW_TOP) bottomRightPixel.y = m_bottomEdgeDistance; else if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_WINDOW_BOTTOM) bottomRightPixel.y = static_cast(windowSize.y) - m_bottomEdgeDistance; else if (m_bottomEdgeAnchor == ANCHOR_VERTICAL_PROPORTIONAL) bottomRightPixel.y = m_bottomEdgeDistance * static_cast(windowSize.y); sf::Vector2f topLeftCoord = mapFloatPixelToCoords( topLeftPixel, (*scene.renderWindow), firstCamera.GetSFMLView()); sf::Vector2f bottomRightCoord = mapFloatPixelToCoords( bottomRightPixel, (*scene.renderWindow), firstCamera.GetSFMLView()); // Move and resize the object according to the anchors if (m_rightEdgeAnchor != ANCHOR_HORIZONTAL_NONE) object->SetWidth(bottomRightCoord.x - topLeftCoord.x); if (m_bottomEdgeAnchor != ANCHOR_VERTICAL_NONE) object->SetHeight(bottomRightCoord.y - topLeftCoord.y); if (m_leftEdgeAnchor != ANCHOR_HORIZONTAL_NONE) object->SetX(topLeftCoord.x + object->GetX() - object->GetDrawableX()); if (m_topEdgeAnchor != ANCHOR_VERTICAL_NONE) object->SetY(topLeftCoord.y + object->GetY() - object->GetDrawableY()); } }