0

I want to scale a 2D sprite to be as large as the collision box for the node.

I have only managed to do it using Node::SetScale and some manual scaling calculation, but I would rather not do it with this method as it is convoluted, since I have to account for that scaling factor for the physical body as well.

However, I could not find a SetScale method for the StaticSprite2D class.

The key code part is:

#if 0
        // Sprite and collision have the same size,
        // like I want it, but I feel this is very convoluted.
        auto rect = boxSprite->GetRectangle();
        auto scaleX = PIXEL_SIZE * rect.Width();
        auto scaleY = PIXEL_SIZE * rect.Height();
        node->SetScale(Vector3(groundWidth / scaleX, groundHeight / scaleY, 0.0f));
        shape->SetSize(Vector2(scaleX, scaleY));
#else
        // Collision shape is correct, but the sprite is smaller
        // so not what I want.
        shape->SetSize(Vector2(groundWidth, groundHeight));
#endif

Full runnable code below, tested with the following boilerplate: https://github.com/cirosantilli/Urho3D-cheat/blob/8c785b38481aa5af48837c5bc3706e46f704ef37/scale_sprite.cpp and Urho3D @ 5e8a275:

#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/Object.h>
#include <Urho3D/Engine/Application.h>
#include <Urho3D/Engine/Engine.h>
#include <Urho3D/Engine/EngineDefs.h>
#include <Urho3D/Graphics/Camera.h>
#include <Urho3D/Graphics/DebugRenderer.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/Graphics/Octree.h>
#include <Urho3D/Graphics/Renderer.h>
#include <Urho3D/Input/Input.h>
#include <Urho3D/Input/InputEvents.h>
#include <Urho3D/Physics/PhysicsEvents.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Scene/Scene.h>
#include <Urho3D/Scene/SceneEvents.h>
#include <Urho3D/Urho2D/CollisionBox2D.h>
#include <Urho3D/Urho2D/CollisionCircle2D.h>
#include <Urho3D/Urho2D/Drawable2D.h>
#include <Urho3D/Urho2D/PhysicsWorld2D.h>
#include <Urho3D/Urho2D/RigidBody2D.h>
#include <Urho3D/Urho2D/Sprite2D.h>
#include <Urho3D/Urho2D/StaticSprite2D.h>

using namespace Urho3D;

class Main : public Application {
    URHO3D_OBJECT(Main, Application);
public:
    Main(Context* context) : Application(context) {
    }
    virtual void Setup() override {
        engineParameters_[EP_FULL_SCREEN] = false;
    }
    void Start() {
        SubscribeToEvent(E_POSTRENDERUPDATE, URHO3D_HANDLER(Main, HandlePostRenderUpdate));
        this->scene_ = new Scene(this->context_);
        scene_->CreateComponent<Octree>();
        scene_->CreateComponent<DebugRenderer>();
        scene_->CreateComponent<PhysicsWorld2D>();
        auto physicsWorld = scene_->GetComponent<PhysicsWorld2D>();
        auto cameraNode_ = scene_->CreateChild("camera");
        cameraNode_->SetPosition(Vector3(0.0f, 0.0f, -1.0f));
        auto camera = cameraNode_->CreateComponent<Camera>();
        camera->SetOrthographic(true);
        camera->SetOrthoSize(4.0);
        auto graphics = GetSubsystem<Graphics>();
        auto renderer = GetSubsystem<Renderer>();
        SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
        renderer->SetViewport(0, viewport);
        auto cache = GetSubsystem<ResourceCache>();
        auto boxSprite = cache->GetResource<Sprite2D>("Urho2D/Box.png");

        auto groundWidth = 2.0;
        auto groundHeight = 2.0;
        auto node = this->scene_->CreateChild("ground");
        node->SetPosition(Vector3(0.0f, 0.0f, 0.0f));
        node->CreateComponent<RigidBody2D>();
        auto shape = node->CreateComponent<CollisionBox2D>();
#if 0
        // Sprite and collision have the same size,
        // like I want it, but I feel this is very convoluted.
        auto rect = boxSprite->GetRectangle();
        auto scaleX = PIXEL_SIZE * rect.Width();
        auto scaleY = PIXEL_SIZE * rect.Height();
        node->SetScale(Vector3(groundWidth / scaleX, groundHeight / scaleY, 0.0f));
        shape->SetSize(Vector2(scaleX, scaleY));
#else
        // Collision shape is correct, but the sprite is smaller
        // so not what I want.
        shape->SetSize(Vector2(groundWidth, groundHeight));
#endif
        auto staticSprite = node->CreateComponent<StaticSprite2D>();
        staticSprite->SetSprite(boxSprite);
    }
    void Stop() {}
private:
    SharedPtr<Scene> scene_;
    void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData) {
        auto physicsWorld = this->scene_->GetComponent<PhysicsWorld2D>();
        physicsWorld->DrawDebugGeometry();
    }
};

URHO3D_DEFINE_APPLICATION_MAIN(Main);

Interestingly, there is a SetScale for the Sprite class, which as far as I've seen in the examples, is used for the UI in 3D scenes.

Superset: arbitrary shaders in 2D:

Also asked at: https://discourse.urho3d.io/t/how-to-scale-a-sprite2d-in-urho3d-without-rescaling-the-entire-node/3785/1

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985

1 Answers1

0

StaticSprite2D::SetDrawRect

This is the method I needed, it sets how big the sprite will be in world coordinates.

The rectangle it takes is centered on the origin, we just give the object lengths and ignore the position.

The full updated working code is present at: https://github.com/cirosantilli/Urho3D-cheat/blob/1905d1f4824c6b88aca867c5be8ccb5dfae957f3/scale_sprite.cpp

The key lines are:

void CreateBox(float x, float y, float width, float height) {
    auto node = this->scene->CreateChild("Box");
    node->SetPosition(Vector3(x, y, 0.0f));
    node->SetRotation(Quaternion(0.0f, 0.0f, 30.0f));
    auto box = node->CreateComponent<CollisionBox2D>();
    box->SetSize(Vector2(width, height));
    box->SetRestitution(0.8);
    box->SetFriction(0.5f);
    box->SetDensity(1.0f);
    auto body = node->CreateComponent<RigidBody2D>();
    body->SetBodyType(BT_DYNAMIC);
    auto staticSprite = node->CreateComponent<StaticSprite2D>();
    staticSprite->SetSprite(this->boxSprite);
    staticSprite->SetDrawRect(Rect(
        width / 2.0f,
        -height / 2.0f,
        -width / 2.0f,
        height / 2.0f
    ));
    staticSprite->SetUseDrawRect(true);
}

Learned from: https://discourse.urho3d.io/t/how-to-scale-a-sprite2d-in-urho3d-without-rescaling-the-entire-node/3785/2

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985