godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.2k stars 20.22k forks source link

Using the RenderingServer to add a texture to the scene, the texture appears at bad position during one frame #94988

Open kakatoto-collab opened 1 month ago

kakatoto-collab commented 1 month ago

Tested versions

4.3-rc1 Mono Win64

System information

Windows 11

Issue description

As said in the title, I use the RenderingServer in order to add texture to the game scene. The problem is that during one frame, the texture appears at wrong position. More precisly, the wrong position is somewhere between the origin (0, 0) and the position it is supposed to be. After this wrong frame, the texture jumps to the expected position.

Steps to reproduce

Launch the attached project and click in the game scene. You will see the green circle popping at bad position during one frame.

Minimal reproduction project (MRP)

BugSpriteJumpingAtInstanciation.zip

clayjohn commented 1 month ago

I don't have things set up to test with C#. But I strongly suspect the problem comes from when the sprite is added. You are creating the CanvasItem and issuing the draw command from the _process() function instead of doing the draw commands during the _draw() function.

In the scene-level API Godot enforces that all drawing operations are done through the _draw() function. I suspect that there is an order of operations issue happening here.

I tried recreating the MRP in GDScript, but I can't reproduce the issue:

extends Node2D

var sprite_sheet: Texture2D

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
    sprite_sheet = load("res://icon.svg")

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
    get_dev_input()

func get_dev_input():
    if Input.is_action_just_pressed("ui_click_left"):
        on_mouse_left_click()

func on_mouse_left_click():
    unsafe_test_draw()

func unsafe_test_draw():
    var src_rect = Rect2(Vector2(0, 0), Vector2(128, 128))
    var rid = RenderingServer.canvas_item_create()
    RenderingServer.canvas_item_set_parent(rid, get_canvas_item())

    var rect = Rect2(Vector2.ZERO, src_rect.size)
    sprite_sheet.draw_rect_region(rid, rect, src_rect)

    var transform_2d = Transform2D.IDENTITY
    transform_2d.origin = get_global_mouse_position()
    RenderingServer.canvas_item_set_transform(rid, transform_2d)
kakatoto-collab commented 1 month ago

Thanks for your reply, following your idea, I changed my code to this :


using Godot;
using System;

public partial class Game : Node2D
{
    public World world;
    public Texture2D Spritesheet;

    public override void _Ready()
    {
        Spritesheet = ResourceLoader.Load("res://Assets/Textures/Spritesheet.png") as Texture2D;

        world = GetNode("%World") as World;
    }

    public override void _Process(double delta)
    {
        QueueRedraw();
    }

    public override void _Draw()
    {
        GetDevInput();
    }

    private void GetDevInput()
    {
        if (Input.IsActionJustPressed("ui_click_left"))
        {
            OnMouseLeftClick();
        }

    }

    private void OnMouseLeftClick()
    {
        UnsafeTestDraw();
    }

    private void UnsafeTestDraw()
    {
        Rect2 srcRect = new Rect2(new Vector2(256, 32), new Vector2(16, 16));
        Rid rid = RenderingServer.CanvasItemCreate();
        RenderingServer.CanvasItemSetParent(rid, world.entityLayer.GetCanvasItem());
        Rect2 rect = new Rect2(Vector2.Zero, srcRect.Size);
        Spritesheet.DrawRectRegion(rid, rect, srcRect);

        Transform2D transform2D = Transform2D.Identity;
        transform2D.Origin = GetGlobalMousePosition();
        RenderingServer.CanvasItemSetTransform(rid, transform2D);
    }
}

but the bug still occurs.

kakatoto-collab commented 1 month ago

BugSpritesJUmpingOnInstanciation

I created a gif so that you can see what happens @clayjohn .

clayjohn commented 1 month ago

Hmmm, let's see if someone from the @godotengine/dotnet team can confirm if this is a C# issue

raulsntos commented 1 month ago

I'm able to reproduce with GDScript. I'll upload the ported MRP, but it's not too different from https://github.com/godotengine/godot/issues/94988#issuecomment-2261665421.

BugSpriteJumpingAtInstanciationGD.zip

clayjohn commented 1 month ago

Thanks @raulsntos I can reproduce the issue with your MRP. I was able to narrow it down to physics interpolation. When you turn off physics interpolation, the issue goes away.

clayjohn commented 1 month ago

I took a quick look at how our CanvasItem code handles physics interpolation to avoid this issue. You have to flag for the RenderingServer that this item shouldn't be interpolated the frame it is created using: RenderingServer.canvas_item_reset_physics_interpolation(rid)

I confirmed in the MRP from RaulSntos that adding that function is all that is needed to fix the issue.

kakatoto-collab commented 1 month ago

I confirm that the bug is solved here by deactivating Physics Interpolation in Project Settings. Thanks guys, great job!

kakatoto-collab commented 1 month ago

Sorry I closed by accident, I am not used to work on Github.

cmagapu commented 1 week ago

@kakatoto-collab I am new to Open Source and would like to contribute. Can I contribute to this?

clayjohn commented 1 week ago

@cmagapu yes! Please go ahead

ghildim commented 6 days ago

@kakatoto-collab Hey, I want to help with this project. How do I start?

kakatoto-collab commented 6 days ago

@kakatoto-collab I am new to Open Source and would like to contribute. Can I contribute to this?

Yes sure, I am not the best to help you to do so though.

cmagapu commented 6 days ago

@clayjohn so the task here is to document how the bug was solved (by deactivating Physics Interpolation in Project Settings), correct? Does that mean I have to raise a PR here: https://github.com/godotengine/godot-docs ?

AThousandShips commented 6 days ago

No the documentation for classes is handled here

ghildim commented 4 days ago

No the documentation for classes is handled here

So should add this to documentation or should we attempt to fix it?