I stumbled upon your channel about a week ago and I just wanted to express how impressed I am with your Godot tutorials. They are incredibly insightful and have been a tremendous help. Thank you so much for sharing your knowledge and expertise! 😊
I've already done the relevant optimizations and will do a followup video in the near future. The trick is to grab the texture from the viewport and assign it to the card, then delete the viewport entirely.
I mistakenly thought this was recreating the types of 3D "cards" like warframe has where when you mouse around them they move and turn and the picture itself it in 3D. Didn't realize this was going to be this simple of a tutorial.
Although I haven't played enough Warframe to see the cards you're talking about, they're probably achieved with a similar effect... plus a perspective shader of some kind to change the interior camera of the viewport as the card itself shifts. I remember seeing a tutorial for it online somewhere a while back.
Hold up! Funny and true. I saw so many tutorials with "don't do this, do this instead" and more like it trying to sound smart instead of trying to teach something
Surprisingly, i use viewport but for the quite simillar problem. my game is built with 3d objects, cards too, and i need to show player preview of card, when they narrow on it. So i made Viewport with other camera and special Card variation made for just displaying same information without scripts. than i use texture from viewport and apply it to CardPreview Sprite2D, showed on main screen
Technically yes, but since the texture is dynamically generated, the normal map would need to be as well. Pre-generated normal maps would require pre-generated textures, which defeats the purpose of this process.
I am actually doing this exact thing for a 2d card game, reason being i need to run full card shaders on my cards so I need to turn the entire card into a single texture. As far as I know only possible with viewports. The main worry is performance since I have worked with viewports in 3.x and it did have performance issues. I assume having say 20 of them active at the same time would not work, but since the cards are somewhat static you could just render it once and store the result as an image. Then just refresh whenever something on the card changes. One downside here is that you can no longer have shaders just on the card art which is within the viewport, but was thinking it might be possible to get around that with a SDF in the shader that defines the area of the art and only letting your shader run on that area. Either way, would be very interested in hearing if you did any performance research on this and had any results?
That's about right, once I hit around twenty viewports onscreen my framerate took a nosedive. As you noted, the trick is to render the viewport to a texture and use that image as the card instead. Some mild tradeoffs, but usually necessary.
Sorry, complete beginner here. I'm using 4.2.1 and when I try to update the text and textures in the set function (I've assigned the resource via the editor), I get an error: "Invalid get index "card_name" (on base: 'Nil')." Probably it has something to do with load order. My script works, when I put it in the _ready() function.
Hard to say without looking at your code. Two things I can think of would be that if you're copying my code directly, I'm using camel case, not snake case, to define and access the variables in the resource. IE, "cardData", not "card_data". Also, you may have forgotten to assign the loaded resource to the local variable data prior to attempting to access it. Finally, note that I'm using get_node directly within the setter; if you're using the dollar sign notation (eg. $node_name assigned with the @onready annotation), then yes, it will fail in the setter because the nodes haven't been initialized yet. That setter runs before the node has been added to the tree, so you have to manually access the nodes.
@@gamegems7658 Thanks for the elaborate answer. Sadly, none of that was causing the error. I can't tell you exactly what it was, but I think it's just trying to load the @onready vars for name, text and texture before they are assigned. I've added code to wait for them and now it just works: if not cardName: await self.ready
@@gamegems7658 Thank you for the elaborate answer. I don't know what it was. Changing the case didn't work. What do you mean by "assign the lodad resource to the local variable prior to attempting to access it"? Currently, I grab the art for example like this: @onready var cardArt: TextureRect = $Front/SubViewport/Card_Template/CardArt I haven't tried manually accessing the node but I might. For now, I just put "await self.ready" in the setter function and that worked. It's probably not the best solution but for now it works.
That's your problem. The setter for the cardData variable runs before _ready()/@onready is called, which means that when the setter attempts to put the relevant data into those nodes, they haven't been assigned yet. Waiting until _ready is... ready is one way to solve the problem, but I think it would be better if you learned the order of operations for node initialization so that this, and other errors like it, don't come up in the future.