godotengine / godot

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

wraparoundLayer : ability to wraparound mirror in positive and negative x and y #15359

Closed blurymind closed 4 years ago

blurymind commented 6 years ago

I am trying to implement a tiling canvas. For that to work, I need to be able to get the 2 bats to tile as 8 bats in at least one of the grid squares:

capture

Here is a minimal example project with bats: tilingProblem.zip

Is that possible to do at all? Its seems like ParalaxLayer can only mirror once in positive x and y. For my design to work, I need it to mirror at least twice (once positive and once negative). As you can see all grid squares are missing either 3 or 2 tiled bats

looking into doing it with parallax layer , I found out that it has a limitation that prevents complete tiling on non-moving screen with sprites moving inside it.

It only tiles partially, its not very good for games where the camera doesn't move, but you need the screen to wraparound. Classic examples can be found in many arcade games, such as pacman, some of the first mario arcades, https://youtu.be/j4lKDTgifl8?t=52s

See this article for a better description: https://en.wikipedia.org/wiki/Wraparound_(video_games)

We can't easily achieve it in godot at the moment. In my case I can't do it via teleporting because it looks ugly - especially on bigger sprites to disappear and reappear. I also want to use it for a tiling texture editor I am making in godot, so that absolutely needs to show both halves of the sprite on two ends of the screen

blurymind commented 6 years ago

I found that in order to achieve it, I need at least three paralax layers: 3paralaxlayers

However this will not work, because the three layers contain different instances of the three test sprites. I need to be able to do this in one paralax layer

akien-mga commented 6 years ago

This used to work in 2.1, no?

blurymind commented 6 years ago

@akien-mga I am using 2.1.4 Is there a way to tell a parallaxLayer to repeat the mirror more than once in x and y? If not- then - it doesnt work. I can make an example project if you want to

Ideally it would be great to have the 2 bats turn into 8 bats inside the original square, but the node has not been designed to do that- it seems to be created to do only a very specific usercase of tiling for a background

akien-mga commented 6 years ago

I am using 2.1.4

It would be good that you keep the issue template and fill it then, you're a seasoned bug reporter now and we should not need to guess your Godot version, OS, steps to reproduce, etc.

blurymind commented 6 years ago

@akien-mga sorry, I felt that this is a limitation of the node, rather than a bug.

Here is a minimal example project with bats: tilingProblem.zip

Ideally I would love to have at least one of the grid squares populated with 8 bats, using just one parallaxLayer - that is currently not possible

It would greatly simplify things if that grid square could be the one that contains the original sprites, but that seems to also be impossible to do, since the mirroring value only goes in positive direction. Would be great if it could go in both negative and positive direction at the same time - that would solve the problem quite well within the origin grid square - tiling would work on all 4 walls of the origin grid square - not just 2

If a fix gets into the 2* branch, you guys will help me finally finish this app I've been making for over a year now :)

vnen commented 6 years ago

BTW, in the editor the parallax won't be shown, it appears only in game.

blurymind commented 6 years ago

@vnen Here is a screenshot of both in editor and in runtime- parallaxLayer failing to create tiling for 2 of the 4 walls of the origin grid square: missingtiling This is a problem

vnen commented 6 years ago

Hm, now I recall seeing something like this. I think when the parallax layer is smaller than the window, it won't mirror correctly.

blurymind commented 6 years ago

@vnen feel free to play with the example project :) tilingProblem.zip

Scaling has not been touched I am interested in tiling inside the origin grid square,where the sprites are - without scrolling

If you can get it to work by changing something in my example, we can perhaps narrow the reason for the problem?

I think the real reason for this is because mirroring is going only in positive x and positive y (yellow circle grid squares). If you make it add 3 more mirror grid clones in the opposite (negative) directions (marked with green circles)- it will solve it: proposedsolution wraparound mode?

but I might be wrong - this could be a bug with tiling

I hope these screenshots clearly communicate what I mean. Please let me know if they dont

reduz commented 6 years ago

This is not a bug, it's working as intended. Maybe it needs to be documented better? Mirroring only happens once..

blurymind commented 6 years ago

@reduz thank you for clarifying. Would it be possible to add a wraparound mode, as described in my last post? It will really help make this feature useful for much more scenarios, such as the ones described.

Is there perhaps a workaround I can use?

reduz commented 6 years ago

@blurymind no.. you will have more luck implementing something custom on your own

reduz commented 6 years ago

this feature is intended to replicate drawing of a scene in different axes, it works nicely even if you have animations or lights, so making it work more generic will not work. It's best you create your own layer that does what you want. Maybe what we could do is a small tutorial on hoooking to camera motion, so you can do your own parallax effects.

reduz commented 6 years ago

Also, probably parallax needs more documentation and a tutorial

blurymind commented 6 years ago

@reduz So do you mean that it needs creating a new node type? To my knowledge godot currently does not offer this functionality via a node!

In that case, this would require a more experienced programmer than me to write it in c++,perhaps base it on the paralaxLayer node

How would you call that new node? a wraparoundLayer node?

blurymind commented 6 years ago

perhaps this could be done with a screen shader? I have been trying to come up with one with no luck :(

I could make a sprite shader that does it to some extent: tilingspriteshader

uniform vec2 offset=vec2(155,44);

vec2 ps = TEXTURE_PIXEL_SIZE;

vec4 tile1 = vec4(tex(TEXTURE,UV-offset*ps).rgb,tex(TEXTURE,UV-offset*ps).a);
vec4 tile2 = vec4(tex(TEXTURE,UV+offset*ps).rgb,tex(TEXTURE,UV+offset*ps).a);
vec4 tile3 = vec4(tex(TEXTURE,UV+offset*vec2(1,-1)*ps).rgb,tex(TEXTURE,UV+offset*vec2(1,-1)*ps).a);
vec4 tile4 = vec4(tex(TEXTURE,UV+offset*vec2(-1,1)*ps).rgb,tex(TEXTURE,UV+offset*vec2(-1,1)*ps).a);

vec4 result = mix(tile1,tile2,tile2.a);
result = mix(result,tile3,tile3.a);
result = mix(result,tile4,tile4.a);

vec4 col = tex(TEXTURE,UV);

COLOR=mix(result,col,col.a);

The problem is that it won't do like that- it needs to be a screen shader to allow me to move the sprites inside .How do I turn it into a screen shader? Any ideas or thats not possible either?

Its such a shame that godot doesn't have a proper wrap behavior

blurymind commented 6 years ago

Ok after playing with this for a while I found that approaching this with a screen shader solution is probably not going to work for me. It looks like godot's screen shaders cant get the alpha channel of what they are affecting. There is also a number of other limitations that would make that solution broken

I could be able to do it if I could get the alpha channel of texscreen, but godot doesnt have that either https://github.com/godotengine/godot/issues/15484

I also tried doing it with _draw(), however there is no way of getting the pixelbuffer of child nodes. I can only quele a screen capture of the whole screen, which is not very useful in this problem. I can get the textures of sprites and draw them with an offset- but what I really need is the pixels of the animatedSpriteNode- being rotated,modulated,scaled, animated- whatever has been done to it - I want its pixels. Godot seems to have no way getting the pixels of a node..

I keep hitting game engine limitations in whatever solution I am trying to do- godot needs a little something missing for any approach I try to implement

I wish I knew more about c++ to be able to create a new wraparoundLayer node, based on the parallaxLayer node. I looked at the code and can't understand exactly what it does. My knowledge in c++ is so poor that I don't even understand the concept of .h and .cpp files

blurymind commented 6 years ago

Here is a solution using the _draw() function and draw_texture method on animatedSprite nodes as children:

func _draw(): 
    for node in ChidSprites():
        var spriteFTex = node.get_sprite_frames().get_frame(node.get_animation(),node.get_frame())#( String anim, int idx ) 
        #right
        draw_texture(spriteFTex,Vector2(node.get_pos().x+get_size().x-spriteFTex.get_width()/2,  node.get_pos().y-spriteFTex.get_height()/2))
        #left
        draw_texture(spriteFTex,Vector2(node.get_pos().x-get_size().x-spriteFTex.get_width()/2,  node.get_pos().y-spriteFTex.get_height()/2))
        #bottom
        draw_texture(spriteFTex,Vector2(node.get_pos().x-spriteFTex.get_width()/2,  node.get_pos().y+get_size().y-spriteFTex.get_height()/2))
        #top
        draw_texture(spriteFTex,Vector2(node.get_pos().x-spriteFTex.get_width()/2,  node.get_pos().y-get_size().y-spriteFTex.get_height()/2))
        #top left
        draw_texture(spriteFTex,Vector2(node.get_pos().x-get_size().x-spriteFTex.get_width()/2, node.get_pos().y-get_size().y-spriteFTex.get_height()/2))
        #bottom left
        draw_texture(spriteFTex,Vector2(node.get_pos().x-get_size().x-spriteFTex.get_width()/2, node.get_pos().y+get_size().y-spriteFTex.get_height()/2))
        #bottom right
        draw_texture(spriteFTex,Vector2(node.get_pos().x+get_size().x-spriteFTex.get_width()/2,  node.get_pos().y+get_size().y-spriteFTex.get_height()/2))
        #top right
        draw_texture(spriteFTex,Vector2(node.get_pos().x+get_size().x-spriteFTex.get_width()/2,  node.get_pos().y-get_size().y-spriteFTex.get_height()/2))

Unfortunately it is too limited and will not work for me because draw_texture can not do any operations on the texture that it is drawing - It can only modulate the color :'((

I wish there was a draw method that draws the node after it has had effects applied to it - rotation, scale, flip, modulate - everything. Modulate is not enough and you have to write quite a long line of code to get it to access the node's resources

@reduz Godot's methods are a bit limited to implement a custom solution.

For the screen shader route - texscreen has no alpha channel- so overlapping clones get an ugly fill bg

For the _draw() route - draw_texture can only modulate color- it can not apply any of the effects that were applied on the origin sprite. There is no method to just get the pixels of the node with effects applied- with alpha preserved. Filed a new request here: https://github.com/godotengine/godot/issues/15501

For the screen capture approach - capturing with backbuffercopy returns a texscreen- which brings us back to the problem with using a screen shader - no alpha channel

blurymind commented 6 years ago

Ok, I came up with a limited solution using _draw(), but my feature request still stands - as it would be definitely very helpful for games with a single screen wraparound behavior. I renamed this issue for more clarity.

Thanks everyone for the patience

KoBeWi commented 5 years ago

So I was just about to post an issue about repeated mirroring for parallax backgrounds. Is it what this issue is about? It's bothersome when you have a texture too small to loop it properly, so you either need to mess with regions or place few sprites next to each other. If we were able to repeat the sprite in parallax X times, it would be much easier.

Xrayez commented 5 years ago

So I was just about to post an issue about repeated mirroring for parallax backgrounds.

@KoBeWi I think this pertains to #27160 more but I see how these are quite connected, posted my workaround hacky snippet there to "fix" this as you described it.

If we were able to repeat the sprite in parallax X times, it would be much easier.

That's also why I put attention on mirror_count (or rather repeat_count) there in #27160 which should also help this issue.

Calinou commented 4 years ago

Closing in favor of https://github.com/godotengine/godot-proposals/issues/658, as feature proposals are now tracked on the Godot proposals repository.