Add networked crab gathering prototype
This commit is contained in:
parent
f55663c105
commit
a62cce6404
56 changed files with 2692 additions and 56 deletions
13
gathering/catalog/crab_blue.tres
Normal file
13
gathering/catalog/crab_blue.tres
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/crab_blue/crab_blue.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"crab_blue"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"crab_net"
|
||||
surface_materials = Array[StringName]([&"sand"])
|
||||
minimum_surface_y = -0.8
|
||||
population = 4
|
||||
26
gathering/catalog/crab_brown.tres
Normal file
26
gathering/catalog/crab_brown.tres
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/crab_brown/crab_brown.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"crab_brown"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"crab_net"
|
||||
surface_materials = Array[StringName]([&"sand"])
|
||||
minimum_surface_y = 0.08
|
||||
population = 2
|
||||
movement_speed = 0.35
|
||||
roam_radius = 3.5
|
||||
scare_radius = 2.8
|
||||
capture_radius = 0.7
|
||||
interaction_range = 2.6
|
||||
charge_duration = 2.0
|
||||
sprite_pixel_size = 0.001
|
||||
sprite_tilt_degrees = -45.0
|
||||
capture_respawn_min_seconds = 480.0
|
||||
capture_respawn_max_seconds = 720.0
|
||||
scare_respawn_min_seconds = 45.0
|
||||
scare_respawn_max_seconds = 90.0
|
||||
minimum_respawn_spacing_seconds = 180.0
|
||||
13
gathering/catalog/crab_dungeness.tres
Normal file
13
gathering/catalog/crab_dungeness.tres
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/crab_dungeness/crab_dungeness.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"crab_dungeness"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"crab_net"
|
||||
surface_materials = Array[StringName]([&"sand"])
|
||||
minimum_surface_y = -0.5
|
||||
population = 4
|
||||
13
gathering/catalog/crab_ghost.tres
Normal file
13
gathering/catalog/crab_ghost.tres
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/crab_ghost/crab_ghost.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"crab_ghost"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"crab_net"
|
||||
surface_materials = Array[StringName]([&"sand"])
|
||||
minimum_surface_y = 0.08
|
||||
population = 4
|
||||
11
gathering/catalog/gatherable_catalog.tres
Normal file
11
gathering/catalog/gatherable_catalog.tres
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[gd_resource type="Resource" script_class="GatherableCatalog" load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_catalog.gd" id="1_catalog"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_brown.tres" id="2_brown"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_ghost.tres" id="3_ghost"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_blue.tres" id="4_blue"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_dungeness.tres" id="5_dungeness"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_catalog")
|
||||
entries = [ExtResource("2_brown"), ExtResource("3_ghost"), ExtResource("4_blue"), ExtResource("5_dungeness")]
|
||||
23
gathering/gatherable_catalog.gd
Normal file
23
gathering/gatherable_catalog.gd
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
class_name GatherableCatalog
|
||||
extends Resource
|
||||
|
||||
const GatherableDataType = preload("res://gathering/gatherable_data.gd")
|
||||
|
||||
@export var entries: Array[GatherableDataType] = []
|
||||
|
||||
|
||||
func get_entry(type_id: StringName) -> GatherableDataType:
|
||||
if type_id.is_empty():
|
||||
return null
|
||||
for entry: GatherableDataType in entries:
|
||||
if entry != null and entry.type_id == type_id:
|
||||
return entry
|
||||
return null
|
||||
|
||||
|
||||
func get_available_entries() -> Array[GatherableDataType]:
|
||||
var available: Array[GatherableDataType] = []
|
||||
for entry: GatherableDataType in entries:
|
||||
if entry != null and entry.is_available():
|
||||
available.append(entry)
|
||||
return available
|
||||
1
gathering/gatherable_catalog.gd.uid
Normal file
1
gathering/gatherable_catalog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bpsncwh7qlfk1
|
||||
61
gathering/gatherable_data.gd
Normal file
61
gathering/gatherable_data.gd
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
class_name GatherableData
|
||||
extends Resource
|
||||
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
|
||||
@export var type_id: StringName
|
||||
@export var catch_data: FishDataType
|
||||
@export var required_tool_id: StringName
|
||||
@export var surface_materials: Array[StringName] = []
|
||||
@export_range(-100.0, 100.0, 0.01) var minimum_surface_y: float = 0.08
|
||||
@export_range(0, 64, 1) var population: int = 0
|
||||
@export_range(0.0, 10.0, 0.05) var movement_speed: float = 0.35
|
||||
@export_range(0.1, 20.0, 0.1) var roam_radius: float = 3.5
|
||||
@export_range(0.1, 20.0, 0.1) var scare_radius: float = 2.8
|
||||
@export_range(0.1, 5.0, 0.05) var capture_radius: float = 0.7
|
||||
@export_range(0.1, 10.0, 0.05) var interaction_range: float = 2.6
|
||||
@export_range(0.1, 10.0, 0.05) var charge_duration: float = 2.0
|
||||
@export_range(0.1, 5.0, 0.01) var sprite_pixel_size: float = 0.001
|
||||
@export_range(-90.0, 90.0, 1.0) var sprite_tilt_degrees: float = -45.0
|
||||
@export_category("Respawn Budget")
|
||||
@export_range(0.0, 3600.0, 1.0) var capture_respawn_min_seconds: float = 480.0
|
||||
@export_range(0.0, 3600.0, 1.0) var capture_respawn_max_seconds: float = 720.0
|
||||
@export_range(0.0, 3600.0, 1.0) var scare_respawn_min_seconds: float = 45.0
|
||||
@export_range(0.0, 3600.0, 1.0) var scare_respawn_max_seconds: float = 90.0
|
||||
@export_range(0.0, 3600.0, 1.0) var minimum_respawn_spacing_seconds: float = 180.0
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not type_id.is_empty()
|
||||
and catch_data != null
|
||||
and catch_data.is_valid_catalog_entry()
|
||||
and catch_data.collection_method != FishDataType.CollectionMethod.FISHING
|
||||
and not required_tool_id.is_empty()
|
||||
and not surface_materials.is_empty()
|
||||
and population > 0
|
||||
and movement_speed >= 0.0
|
||||
and roam_radius > 0.0
|
||||
and scare_radius > 0.0
|
||||
and capture_radius > 0.0
|
||||
and interaction_range > 0.0
|
||||
and charge_duration > 0.0
|
||||
and sprite_pixel_size > 0.0
|
||||
and capture_respawn_max_seconds >= capture_respawn_min_seconds
|
||||
and scare_respawn_max_seconds >= scare_respawn_min_seconds
|
||||
)
|
||||
|
||||
|
||||
func get_respawn_delay(reason: StringName, rng: RandomNumberGenerator) -> float:
|
||||
var minimum_seconds: float = capture_respawn_min_seconds
|
||||
var maximum_seconds: float = capture_respawn_max_seconds
|
||||
if reason == &"scared":
|
||||
minimum_seconds = scare_respawn_min_seconds
|
||||
maximum_seconds = scare_respawn_max_seconds
|
||||
if rng == null or maximum_seconds <= minimum_seconds:
|
||||
return minimum_seconds
|
||||
return rng.randf_range(minimum_seconds, maximum_seconds)
|
||||
|
||||
|
||||
func is_available() -> bool:
|
||||
return catch_data != null and catch_data.active and is_valid()
|
||||
1
gathering/gatherable_data.gd.uid
Normal file
1
gathering/gatherable_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://doheo0308illx
|
||||
247
gathering/gathering_controller.gd
Normal file
247
gathering/gathering_controller.gd
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
class_name GatheringController
|
||||
extends Node3D
|
||||
|
||||
const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
|
||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||
const NetworkWorldSpawnServiceType = preload(
|
||||
"res://network/network_world_spawn_service.gd"
|
||||
)
|
||||
|
||||
signal status_changed(message: String)
|
||||
|
||||
@export_range(0.5, 5.0, 0.05) var marker_distance: float = 1.7
|
||||
@export_range(0.1, 2.0, 0.05) var marker_radius: float = 0.7
|
||||
@export_flags_3d_physics var terrain_collision_mask: int = 1
|
||||
@export_range(0.5, 20.0, 0.5) var ray_height: float = 5.0
|
||||
@export_range(0.5, 20.0, 0.5) var ray_depth: float = 8.0
|
||||
|
||||
var _player: Player
|
||||
var _bag: PlayerBag
|
||||
var _hotbar: PlayerHotbar
|
||||
var _fishing_spot: FishingSpotType
|
||||
var _service: NetworkWorldSpawnServiceType
|
||||
var _marker: MeshInstance3D
|
||||
var _marker_invalid_material: StandardMaterial3D
|
||||
var _marker_valid_material: StandardMaterial3D
|
||||
var _marker_position: Vector3
|
||||
var _marker_has_surface: bool = false
|
||||
var _target_entity_id: String = ""
|
||||
var _charging: bool = false
|
||||
var _charge_elapsed: float = 0.0
|
||||
var _charge_duration: float = 2.0
|
||||
var _request_id: String = ""
|
||||
var _gameplay_input_enabled: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_build_marker()
|
||||
set_process_unhandled_input(true)
|
||||
|
||||
|
||||
func setup(
|
||||
player: Player,
|
||||
bag: PlayerBag,
|
||||
hotbar: PlayerHotbar,
|
||||
fishing_spot: FishingSpotType,
|
||||
service: NetworkWorldSpawnServiceType,
|
||||
) -> void:
|
||||
_player = player
|
||||
_bag = bag
|
||||
_hotbar = hotbar
|
||||
_fishing_spot = fishing_spot
|
||||
_service = service
|
||||
_service.local_interaction_finished.connect(
|
||||
_on_interaction_finished
|
||||
)
|
||||
|
||||
|
||||
func set_gameplay_input_enabled(enabled: bool) -> void:
|
||||
_gameplay_input_enabled = enabled
|
||||
if not enabled:
|
||||
_cancel_charge()
|
||||
|
||||
|
||||
func is_net_selected() -> bool:
|
||||
return (
|
||||
_bag != null
|
||||
and _hotbar != null
|
||||
and _hotbar.get_selected_item_id()
|
||||
== FishingShopStockType.CRAB_NET_ID
|
||||
and _bag.owns_item(FishingShopStockType.CRAB_NET_ID)
|
||||
)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var input_available: bool = (
|
||||
_gameplay_input_enabled
|
||||
and is_net_selected()
|
||||
and _player != null
|
||||
and _player.is_local_control_enabled()
|
||||
and _fishing_spot != null
|
||||
and _fishing_spot.can_change_hotbar_selection()
|
||||
)
|
||||
if not input_available:
|
||||
if _charging:
|
||||
_cancel_charge()
|
||||
_set_marker_visible(false)
|
||||
return
|
||||
if not _charging:
|
||||
_set_marker_visible(false)
|
||||
return
|
||||
_charge_elapsed += delta
|
||||
_update_marker_target()
|
||||
_update_target_entity()
|
||||
var target_entry: GatherableData = _service.get_entry_for_entity(
|
||||
_target_entity_id
|
||||
)
|
||||
if target_entry != null:
|
||||
_charge_duration = target_entry.charge_duration
|
||||
var fully_charged: bool = _charge_elapsed >= _charge_duration
|
||||
var valid_target: bool = (
|
||||
fully_charged
|
||||
and not _target_entity_id.is_empty()
|
||||
and _player.is_sneaking()
|
||||
)
|
||||
_marker.material_override = (
|
||||
_marker_valid_material if valid_target else _marker_invalid_material
|
||||
)
|
||||
_set_marker_visible(_marker_has_surface)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not event.is_action("fish_primary"):
|
||||
return
|
||||
if event.is_pressed():
|
||||
if (
|
||||
_charging
|
||||
or not _gameplay_input_enabled
|
||||
or not is_net_selected()
|
||||
or _fishing_spot == null
|
||||
or not _fishing_spot.can_change_hotbar_selection()
|
||||
):
|
||||
return
|
||||
_request_id = _service.begin_local_interaction()
|
||||
if _request_id.is_empty():
|
||||
return
|
||||
_charging = true
|
||||
_charge_elapsed = 0.0
|
||||
_charge_duration = maxf(
|
||||
_service.get_charge_duration_for_tool(
|
||||
FishingShopStockType.CRAB_NET_ID
|
||||
),
|
||||
0.1,
|
||||
)
|
||||
_update_marker_target()
|
||||
_update_target_entity()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if not _charging:
|
||||
return
|
||||
var fully_charged: bool = _charge_elapsed >= _charge_duration
|
||||
if (
|
||||
fully_charged
|
||||
and _marker_has_surface
|
||||
and not _target_entity_id.is_empty()
|
||||
and _player.is_sneaking()
|
||||
):
|
||||
_service.finish_local_interaction(
|
||||
_request_id,
|
||||
_target_entity_id,
|
||||
_marker_position,
|
||||
)
|
||||
else:
|
||||
_service.cancel_local_interaction(_request_id)
|
||||
status_changed.emit(
|
||||
"Sneak close, pull the net all the way back, and line up the marker."
|
||||
)
|
||||
_reset_charge_state()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _update_marker_target() -> void:
|
||||
_marker_has_surface = false
|
||||
if _player == null or get_world_3d() == null:
|
||||
return
|
||||
var facing: Vector3 = _player.get_facing_direction()
|
||||
facing.y = 0.0
|
||||
if facing.is_zero_approx():
|
||||
facing = -_player.global_basis.z
|
||||
facing.y = 0.0
|
||||
if facing.is_zero_approx():
|
||||
return
|
||||
facing = facing.normalized()
|
||||
var query_center: Vector3 = _player.global_position + facing * marker_distance
|
||||
var query := PhysicsRayQueryParameters3D.create(
|
||||
query_center + Vector3.UP * ray_height,
|
||||
query_center - Vector3.UP * ray_depth,
|
||||
terrain_collision_mask,
|
||||
)
|
||||
query.collide_with_areas = false
|
||||
query.collide_with_bodies = true
|
||||
var hit: Dictionary = get_world_3d().direct_space_state.intersect_ray(query)
|
||||
if hit.is_empty():
|
||||
return
|
||||
var position: Variant = hit.get("position")
|
||||
if typeof(position) != TYPE_VECTOR3:
|
||||
return
|
||||
_marker_position = (position as Vector3) + Vector3.UP * 0.045
|
||||
_marker.global_position = _marker_position
|
||||
_marker_has_surface = true
|
||||
|
||||
|
||||
func _update_target_entity() -> void:
|
||||
_target_entity_id = (
|
||||
_service.find_capture_target(
|
||||
_marker_position,
|
||||
FishingShopStockType.CRAB_NET_ID,
|
||||
)
|
||||
if _marker_has_surface and _service != null
|
||||
else ""
|
||||
)
|
||||
|
||||
|
||||
func _cancel_charge() -> void:
|
||||
if _charging and not _request_id.is_empty() and _service != null:
|
||||
_service.cancel_local_interaction(_request_id)
|
||||
_reset_charge_state()
|
||||
|
||||
|
||||
func _reset_charge_state() -> void:
|
||||
_charging = false
|
||||
_charge_elapsed = 0.0
|
||||
_request_id = ""
|
||||
_target_entity_id = ""
|
||||
_marker_has_surface = false
|
||||
_set_marker_visible(false)
|
||||
|
||||
|
||||
func _on_interaction_finished(accepted: bool, message: String) -> void:
|
||||
_reset_charge_state()
|
||||
if not accepted or not message.is_empty():
|
||||
status_changed.emit(message)
|
||||
|
||||
|
||||
func _build_marker() -> void:
|
||||
_marker_invalid_material = StandardMaterial3D.new()
|
||||
_marker_invalid_material.albedo_color = Color(0.86, 0.24, 0.19, 0.9)
|
||||
_marker_invalid_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
_marker_valid_material = StandardMaterial3D.new()
|
||||
_marker_valid_material.albedo_color = Color(0.25, 0.9, 0.36, 0.95)
|
||||
_marker_valid_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
var ring := TorusMesh.new()
|
||||
ring.inner_radius = marker_radius * 0.78
|
||||
ring.outer_radius = marker_radius
|
||||
ring.rings = 16
|
||||
ring.ring_segments = 24
|
||||
_marker = MeshInstance3D.new()
|
||||
_marker.name = "GatheringTargetMarker"
|
||||
_marker.mesh = ring
|
||||
_marker.material_override = _marker_invalid_material
|
||||
_marker.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_marker.visible = false
|
||||
add_child(_marker)
|
||||
|
||||
|
||||
func _set_marker_visible(value: bool) -> void:
|
||||
if _marker != null:
|
||||
_marker.visible = value
|
||||
1
gathering/gathering_controller.gd.uid
Normal file
1
gathering/gathering_controller.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cec7nwk815d8a
|
||||
113
gathering/world_gatherable.gd
Normal file
113
gathering/world_gatherable.gd
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
class_name WorldGatherable
|
||||
extends Node3D
|
||||
|
||||
const GatherableDataType = preload("res://gathering/gatherable_data.gd")
|
||||
|
||||
var entity_id: String = ""
|
||||
var type_id: StringName
|
||||
var data: GatherableDataType
|
||||
var _sprite: Sprite3D
|
||||
var _target_position: Vector3
|
||||
var _target_yaw: float = 0.0
|
||||
var _has_state: bool = false
|
||||
var _despawning: bool = false
|
||||
|
||||
|
||||
func configure(
|
||||
configured_entity_id: String,
|
||||
configured_data: GatherableDataType,
|
||||
position: Vector3,
|
||||
yaw: float,
|
||||
) -> void:
|
||||
entity_id = configured_entity_id
|
||||
data = configured_data
|
||||
type_id = data.type_id if data != null else StringName()
|
||||
_ensure_visual()
|
||||
if data != null and data.catch_data != null:
|
||||
_sprite.texture = data.catch_data.display_texture
|
||||
_sprite.pixel_size = data.sprite_pixel_size
|
||||
_sprite.rotation_degrees.x = data.sprite_tilt_degrees
|
||||
apply_network_state(position, yaw, true)
|
||||
|
||||
|
||||
func apply_network_state(
|
||||
position: Vector3,
|
||||
yaw: float,
|
||||
immediate: bool = false,
|
||||
) -> void:
|
||||
if _despawning or not position.is_finite() or not is_finite(yaw):
|
||||
return
|
||||
_target_position = position
|
||||
_target_yaw = yaw
|
||||
if immediate or not _has_state:
|
||||
global_position = position
|
||||
rotation.y = yaw
|
||||
_has_state = true
|
||||
|
||||
|
||||
func play_despawn(with_dust: bool) -> void:
|
||||
if _despawning:
|
||||
return
|
||||
_despawning = true
|
||||
if with_dust:
|
||||
_emit_dust()
|
||||
var tween: Tween = create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.set_trans(Tween.TRANS_QUAD)
|
||||
tween.set_ease(Tween.EASE_IN)
|
||||
if _sprite != null:
|
||||
tween.tween_property(_sprite, "position:y", -0.3, 0.3)
|
||||
tween.tween_property(_sprite, "scale", Vector3(0.75, 0.75, 0.75), 0.3)
|
||||
tween.chain().tween_callback(queue_free)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not _has_state or _despawning:
|
||||
return
|
||||
global_position = global_position.lerp(
|
||||
_target_position,
|
||||
1.0 - exp(-10.0 * delta),
|
||||
)
|
||||
rotation.y = lerp_angle(
|
||||
rotation.y,
|
||||
_target_yaw,
|
||||
1.0 - exp(-8.0 * delta),
|
||||
)
|
||||
|
||||
|
||||
func _ensure_visual() -> void:
|
||||
if _sprite != null:
|
||||
return
|
||||
_sprite = Sprite3D.new()
|
||||
_sprite.name = "GatherableSprite"
|
||||
_sprite.position.y = 0.22
|
||||
_sprite.shaded = false
|
||||
_sprite.double_sided = true
|
||||
_sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
add_child(_sprite)
|
||||
|
||||
|
||||
func _emit_dust() -> void:
|
||||
var particles := CPUParticles3D.new()
|
||||
particles.name = "DustPoof"
|
||||
particles.amount = 9
|
||||
particles.lifetime = 0.55
|
||||
particles.one_shot = true
|
||||
particles.explosiveness = 1.0
|
||||
particles.direction = Vector3.UP
|
||||
particles.spread = 58.0
|
||||
particles.gravity = Vector3(0.0, -2.4, 0.0)
|
||||
particles.initial_velocity_min = 0.65
|
||||
particles.initial_velocity_max = 1.25
|
||||
particles.scale_amount_min = 0.7
|
||||
particles.scale_amount_max = 1.35
|
||||
var dust_material := StandardMaterial3D.new()
|
||||
dust_material.albedo_color = Color(0.58, 0.46, 0.31, 0.85)
|
||||
dust_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
dust_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
var dust_mesh := BoxMesh.new()
|
||||
dust_mesh.size = Vector3(0.1, 0.1, 0.1)
|
||||
dust_mesh.material = dust_material
|
||||
particles.mesh = dust_mesh
|
||||
add_child(particles)
|
||||
particles.emitting = true
|
||||
1
gathering/world_gatherable.gd.uid
Normal file
1
gathering/world_gatherable.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://3t8ygawpkgwj
|
||||
Loading…
Add table
Add a link
Reference in a new issue