Good evening. I want to write a script to randomize the texture of a 3D model (MeshInnstance) in Godot 4 using Spritesheet (each texture is 18 by 18 pixels in size, the width of the image with textures is 54, with a height of 18 pixels).
@onready var Material_texture_block = $"../Куб"
@onready var Random_texture_block = $"."
var frames: int
func _ready() -> void:
frames = int(texture.get_width() / region_rect.size.x)
var random_index = randi_range(0, frames - 1)
region_rect.position.x = float(random_index * region_rect.size.x)
if Material_texture_block == null:
return
var material = Material_texture_block.get_surface_override_material(0)
if material == null:
material = StandardMaterial3D.new() Material_texture_block.set_surface_override_material(0, material)
if material is StandardMaterial3D:
var image = texture.get_image()
var cropped_image = Image.new() cropped_image.copy_from(image)
var crop_rect = Rect2i( int(region_rect.position.x), int(region_rect.position.y), int(region_rect.size.x), int(region_rect.size.y)
)
cropped_image.crop(crop_rect)
var new_texture = ImageTexture.create_from_image(cropped_image)
material.albedo_texture = new_texture
At the moment, the previous texture image remains unchanged and does not change. How can I fix the script so that it works correctly?
If your UVs are set up correctly you can scale the texture coordinates.
@export var h_frames: int = 3
@export var v_frames: int = 1
func _ready() -> void:
var material := StandardMaterial3D.new()
# scale material based on frame count
material.uv1_scale = Vector3(1.0/h_frames, 1.0/v_frames, 1.0)
# picking random frames
var random_h_frame: int = randi_range(0, h_frames)
var random_v_frame: int = randi_range(0, v_frames)
# offset determined by `frame * scale`
var x_offset: float = random_h_frame * material.uv1_scale.x
var y_offset: float = random_v_frame * material.uv1_scale.y
material.uv1_offset = Vector3(x_offset, y_offset, 0.0)
Cropping a new image is a expensive operation. Though .crop should take a rect, it only takes a width and height. Hence “expected 2 arguments but recieved 1”, it wants two integers.
It’ll be the model’s material’s texture, probably better to put script on one of the two you asked about, a sibling could produce strange results on-ready
# assuming on the root node "block_test"
material.albedo_texture = load("your texture file.png") # or through another @export
$Ky6.material_override = material
Thank you. I’m sorry, I’m still new to working in the Godot 4 3D project/ Do I need to write these lines in the MeshInstance script or still in the Sprite2D script?
In that example, since it’s using the path $Ky6 it assumes that your MeshInstance3D named “Ky6” is a child of the script, so the only node that could have that script is the root node “block_test”
If you change the path to $"../Ky6" then it could work on the Sprite2D script still, as that path goes up a parent (../) then down to a sibling (Ky6)