Godot 重复中断脚本

Godot repeating breaks script

我正在 Godot 中制作模式 7/透视投影项目。当我 运行 它时,它会产生预期的效果,将 2d 图像显示为 3d 平面。 代码:

func _ready():
map.load("res://map2.png")
perspective.load("res://map2.png")
for px in 1:
    self.texture = display
    for y in map.get_height():
        _y = (y + py - 1)/z
        for x in map.get_width():
            _x = (x + px)/z
            map.lock()
            pix = map.get_pixel(_x, _y)
            map.unlock()
            perspective.lock()
            perspective.set_pixel(x, y, pix)
            perspective.unlock()
        display.create_from_image(perspective)
        z += 1

图片:

但是,我有一个问题。我在 ready 函数中有代码,在 for 循环中。我希望每一帧都调用它,但是当我将重复次数从一增加到两次时,它会将整个图像变成红色。我不知道是什么原因造成的。一种猜测是我没有正确锁定和解锁图像,但情况很可能并非如此。另一个猜测是 x 和 y 变量不是每次都重置,但也可以正常工作。我不认为循环本身是问题,但我不知道哪里出了问题。

我努力编写你的代码 运行。我中途放弃了,转而使用锁定位实现了 中的逻辑。这是代码:

extends Sprite

export(Transform) var matrix:Transform
var sampler:Image
var buffer:Image
var size:Vector2
var center:Vector2

func _ready():
    sampler = texture.get_data()
    var err = sampler.decompress()
    if err != OK:
        push_error("Failed to decompress texture")
        return

    size = Vector2(texture.get_width(), texture.get_height())
    center = size * 0.5

    buffer = Image.new()
    buffer.create(int(size.x), int(size.y), false, Image.FORMAT_RGBA8)

func _process(_delta):
    #matrix = matrix.rotated(Vector3.RIGHT, 0.01)
    
    sampler.lock()
    buffer.lock()
    for y in size.x:
        for x in size.y:
            var uv:Vector3 = matrix * Vector3(x - center.x, y - center.y, 1.0)
            
            if uv.z <= 0.0:
                buffer.set_pixel(x, y, Color.transparent)
                continue
            
            var _x = (uv.x / uv.z) + center.x
            var _y = (uv.y / uv.z) + center.y
            if _x < 0.0 or _x >= size.x or _y < 0.0 or _y >= size.y:
                buffer.set_pixel(x, y, Color.transparent)
                continue

            #buffer.set_pixel(x, y, Color(_x / size.x, y / size.y, 0.0))
            buffer.set_pixel(x, y, sampler.get_pixel(_x, _y))
    buffer.unlock()
    sampler.unlock()

    var display = ImageTexture.new()
    display.create_from_image(buffer, 0)
    self.texture = display

如您所见,我正在导出 Transfrom 以供编辑器使用。那是一个适当的 3D 变换。 _process 上有注释行是做旋转的,试试看。

sampler ImageSpriteTexture 的副本(副本是在 _ready 上制作的)。而 buffer Image 是构建要显示的内容的地方。

代码正在从 buffer 创建一个 ImageTexture 并用它替换当前的 texture,每一帧(在 _process 上)。我将标志设置为 0,因为 FLAG_REPEATFLAG_FILTER 模糊了 Sprite.

对面的边界

矢量Vector2 size保存纹理的大小。而Vector2 Center就是圆心的坐标。


正如我一开始所说,这是我之前回答的逻辑。这一行:

vec3 uv = matrix * vec3(UV - 0.5, 1.0);

等同于(除了我没有将坐标缩放到 0 到 1 的范围):

var uv:Vector3 = matrix * Vector3(x - center.x, y - center.y, 1.0)

然后我有这一行:

if (uv.z < 0.0) discard;

结果是这样的:

if uv.z <= 0.0:
    buffer.set_pixel(x, y, Color.transparent)
    continue

我设置透明是因为我没有重新创建缓冲区,也没有事先清除它。

最后这一行:

COLOR = texture(TEXTURE, (uv.xy / uv.z) + 0.5);

结果是这样的:

var _x = (uv.x / uv.z) + center.x
var _y = (uv.y / uv.z) + center.y
if _x < 0.0 or _x >= size.x or _y < 0.0 or _y >= size.y:
    buffer.set_pixel(x, y, Color.transparent)
    continue

buffer.set_pixel(x, y, sampler.get_pixel(_x, _y))

根据结果,这是“在 3D 中旋转”的 Godot 图标(不是真的,但就是这个想法):

请忽略 GIF 编码造成的视觉伪影。

我不确定你是否愿意坚持我之前回答的逻辑。不过,我相信这个应该不会太难修改以满足您的需要。


附录

我使用了 Transform,因为没有方便的 Matrix 类型可用。但是,转换在内部使用矩阵。另见 Transformation matrix.

Mode 7 formula according to Wikipedia 使用 2 x 2 矩阵,它比我这里的更简单。但是,无论如何,您将需要 Matrix 和 Vector 的乘积。您不能独立计算组件。

这是根据维基百科的公式:

r' = M*(r - r_0) + r_0

即:

var rp = mult(M, r - r_0) + r_0

其中 mult 看起来像这样:

func mult(matrix, vector:Vector2) -> Vector2:
    var x = vector.x * matrix.a + vector.y * matrix.b
    var y = vector.x * matrix.c + vector.y * matrix.d
    return Vector2(x, y)

但是,正如我所说,没有方便的矩阵类型。如果我们导出 abcd,我们有:

var rp = mult(a, b, c, d, r - r_0) + r_0

mult 看起来像这样:

func mult(a:float, b:float, c:float, d:float, vector:Vector2) -> Vector2:
    var x = vector.x * a + vector.y * b
    var y = vector.x * c + vector.y * d
    return Vector2(x, y)

我们可以很容易地使用修改代码来做到这一点。首先导出 abcd,如我所说:

export(float) var a:float
export(float) var b:float
export(float) var c:float
export(float) var d:float

这是 _process 修改:

func _process(_delta):
    sampler.lock()
    buffer.lock()
    for y in size.x:
        for x in size.y:
            var rp = mult(a, b, c, d, Vector2(x, y) - center) + center
            if rp.x < 0.0 or rp.x >= size.x or rp.y < 0.0 or rp.y >= size.y:
                buffer.set_pixel(x, y, Color.transparent)
                continue

            buffer.set_pixel(x, y, sampler.get_pixel(rp.x, rp.y))
    buffer.unlock()
    sampler.unlock()

    var display = ImageTexture.new()
    display.create_from_image(buffer, 6)
    self.texture = display

当然,mult就是我上面展示的那个。 我在这里假设 r_0 就是我所说的 center

我不确定如何解释 abcd,所以这里是 a = 1b = 2, c = 3d = 4: