Skip to content

Image-to-region-Godot

Sử dụng map trắng đen và có ShapeBurst được tạo bằng QGIS, sau code trên Godot Engine để cho phép chọn và highlight các region, province trong map.

Created date: 2023-05-30

1 Image-to-region-Godot

Để chuyển từ 1 sprite image 2D sáng multiple sprite thì cần phải tách từng sprite sang dạng opaque 100% cho mỗi sub sprite

Phù hợp trong tách region của bản đồ, như các quốc gia trên thế giới, sau đó dùng opaque_to_polygons của bitmap để tách từng polygon ra.

Khi sử dụng QGIS cần chú ý phải dùng ShapeBurst Fill của Layer Styling

  • Dùng 2 màu, màu 1 là transparent
  • Màu 2 bất kỳ
  • Chọn set distance > 3.0 để phân biệt rõ rệt các region của polygon
  • Chọn blur Strength khoảng 5
  • Tạo layer và export sang SVG để dùng cho Godot

Dưới đây là code để tách region

extends Node2D
@onready var node_to_save = $"."
var scene = PackedScene.new()
@export var hide_raw_map:bool

@onready var map_sprite: Sprite2D = $MapSprite

func _ready() -> void:
    create_polygon_from_sprite(map_sprite)
    scene.pack(node_to_save)
    ResourceSaver.save(scene, "res://test/MyScene2.tscn")

func create_polygon_from_sprite(sprite):
    # Get the sprite's texture.
    var texture = sprite.texture
    # Get the sprite texture's size.
    var texture_size = sprite.texture.get_size()
    # Get the image from the sprite's texture.
    var image = texture.get_image()

    # Create a new bitmap.
    var bitmap = BitMap.new()
    # Create the bitmap from the image. We set the minimum alpha threshold.
    bitmap.create_from_image_alpha(image, 0.1) # 0.1 (default threshold).
    # Get the rect of the bitmap.
    var bitmap_rect = Rect2(Vector2(0, 0), bitmap.get_size())
    # Grow the bitmap if you need (we don't need it in this case).
    #bitmap.grow_mask(0, bitmap_rect) # 2
    # Convert all the opaque parts of the bitmap into polygons.
    var polygons = bitmap.opaque_to_polygons(bitmap_rect, 1) # 2 (default epsilon).
    print("There are %s region extracted from the image"%(polygons.size()))
    # Check if there are polygons.
    if polygons.size() > 1:
        # Loop through all the polygons.
        #map_sprite.visible = hide_raw_map
        for i in range(polygons.size()):
            # Create a new 'Polygon2D'.
            var polygon = Polygon2D.new()
            # Set the polygon and texture
            polygon.polygon = polygons[i]
            polygon.texture = texture
            # Check if the sprite is centered to get the proper position.
            if sprite.centered:
                polygon.position = sprite.position - (texture_size / 2)
            else:
                polygon.position = sprite.position
            # Take the sprite's scale into account and apply it to the position.
            polygon.scale = sprite.scale
            polygon.position *= polygon.scale
            polygon.name = "country_%s"%i

            # Create Area2d and collision shape
            var area = Area2D.new()
            area.name = "area_country_%s"%i
            var collision = CollisionPolygon2D.new()
            collision.name = "coll_country_%s"%i
            collision.polygon = polygon.get_polygon() # return the PackedVector2Array
            collision.scale = polygon.scale
            collision.position = polygon.position
            # add to scene
            node_to_save.add_child(area)
            area.add_child(collision)
            #collision.add_child(polygon)

            collision.set_owner(node_to_save)
            #polygon.set_owner(node_to_save)
            area.set_owner(node_to_save)
    else:
        print("Cannot extract the region from image")

2 Tạo Clickable region

func _ready() -> void:
    create_polygon_from_sprite(map_sprite)
    scene.pack(node_to_save)
    ResourceSaver.save(scene, "res://test/MyScene2.tscn")
    for area_item in country_area_list:
        print(area_item.name)
        var polygon = area_item.get_node("Polygon2D")
        area_item.mouse_entered.connect(_on_Area2D_mouse_entered.bind(area_item))
        area_item.mouse_exited.connect(_on_Area2D_mouse_exited.bind(area_item))


func _on_Area2D_mouse_entered(area):
    area.modulate = Color(0, 1, 0)

func _on_Area2D_mouse_exited(area):
    area.modulate = Color(1, 1, 1)

Trong Godot 4, cần phải sử dụng bind để xác định các arguments sẽ được áp dụng trong function. Nếu không sử dụngg bind(area_item) thì toàn bộ area_item sẽ bị ảnh hưởng

Vậy là sau khoảng 3-4 ngày thì mình đã tìm được cách tách region từ map.
Trong game có lẽ sẽ tạo 1 fantasy map để hiển thị. Còn các map region này chỉ giúp select cho dễ

No other pages link to this page.



Created : May 30, 2023