@tool extends Node3D func get_resource_name(resource: Resource) -> String: return resource.resource_path.get_file().trim_suffix('.tres') #todo 性能 func snap_vector3(value: Vector3) -> Vector3: return Vector3(snap_float(value.x), snap_float(value.y), snap_float(value.z)) func snap_float(value: float) -> float: return floor(value/Setting.pixel_size) * Setting.pixel_size func dir_angle(dir: Vector2) -> float: dir.x = abs(dir.x) return dir.angle_to(Vector2.RIGHT) func vector_reduce(vector: Vector2, reduce: float) -> Vector2: var length: float = vector.length() if length == 0: return vector var new_len = max(length - reduce, 0) var scale_rate = new_len / length return vector * scale_rate func raycast_wall(from: Vector3, to: Vector3) -> bool: var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state var query: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, to, 1) var result: Dictionary = space_state.intersect_ray(query) return result.size()>0 func raycast_character(shape: Shape3D, origin: Vector3, dir: Vector2) -> Array[Character]: var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state var query: PhysicsShapeQueryParameters3D = PhysicsShapeQueryParameters3D.new() var angle: float = dir_angle(dir) query.shape = shape query.transform.origin = origin query.transform.basis = query.transform.basis.rotated(Vector3.UP, angle) var result: Array[Dictionary] = space_state.intersect_shape(query) var result2: Array = result.map(func(v): return v["collider"]).filter(func(v): return v is Character) var result3: Array[Character] for node in result2: result3.append(node as Character) if result.size() > result3.size(): result3.append(null) #特殊处理 表示命中非Character物体 例如墙体或地面 return result3 func clean_animation_lib(): #删除技能配置中没有的animation文件 #todo 非阻断 有空再说 pass func refresh_animation_lib(): #刷新animation库 使其与animation文件同步 var animation_library_path: String = "res://resource/skill_animation_library/animation_library.tres" var animation_library = load(animation_library_path) var dir_path: String = "res://resource/skill_animation" var dir: DirAccess = DirAccess.open(dir_path) for file in dir.get_files(): var path: String = dir_path + "/" + file var res = load(path) if res is Animation: var animation: Animation = res as Animation var animation_name: String = Util.get_resource_name(animation) print("refresh_animation_lib: ", animation_name) animation_library.add_animation(animation_name, animation) animation_library.animation_added.emit(animation_name) ResourceSaver.save(animation_library, animation_library_path) func refresh_mesh_library(path_list: Array, with_clear: bool = false): print("refresh_mesh_library") var default_shape_normal_full: Array[Variant] = [load("res://resource/mesh_library/default_shape_normal_full.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_normal_half: Array[Variant] = [load("res://resource/mesh_library/default_shape_normal_half.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_normal_stair1: Array[Variant] = [load("res://resource/mesh_library/default_shape_normal_stair1.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_normal_stair2: Array[Variant] = [load("res://resource/mesh_library/default_shape_normal_stair2.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_large_full: Array[Variant] = [load("res://resource/mesh_library/default_shape_large_full.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_large_half: Array[Variant] = [load("res://resource/mesh_library/default_shape_large_half.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_large_stair1: Array[Variant] = [load("res://resource/mesh_library/default_shape_large_stair1.tres") as Shape3D, Transform3D.IDENTITY] var default_shape_large_stair2: Array[Variant] = [load("res://resource/mesh_library/default_shape_large_stair2.tres") as Shape3D, Transform3D.IDENTITY] var mesh_library_level: MeshLibrary = load("res://resource/mesh_library/mesh_library_level.tres") as MeshLibrary var mesh_library_ground: MeshLibrary = load("res://resource/mesh_library/mesh_library_ground.tres") as MeshLibrary var mesh_library_level_id_list: Array[int] = [] var mesh_library_ground_id_list: Array[int] = [] for file_name_full in path_list: var mesh_name = file_name_full.get_file().split('-')[0].trim_suffix('.vox') print(mesh_name) var is_ground = mesh_name.begins_with("g_") if is_ground: mesh_name = mesh_name.trim_prefix("g_") var mesh: Mesh = load(file_name_full) as Mesh var mesh_library: MeshLibrary = mesh_library_ground if is_ground else mesh_library_level var mesh_library_id_list: Array[int] = mesh_library_ground_id_list if is_ground else mesh_library_level_id_list var mesh_id: int = mesh_library.find_item_by_name(mesh_name) if mesh_id == -1: mesh_id = mesh_library.get_last_unused_item_id() mesh_library.create_item(mesh_id) mesh_library_id_list.append(mesh_id) mesh_library.set_item_name(mesh_id, mesh_name) mesh_library.set_item_mesh(mesh_id, mesh) var name_prefix = mesh_name.split("_")[0] if mesh_name.contains("_") else mesh_name var item_shapes: Array match name_prefix: "f": item_shapes = default_shape_normal_full if not is_ground else default_shape_large_full "h": item_shapes = default_shape_normal_half if not is_ground else default_shape_large_half "s1": item_shapes = default_shape_normal_stair1 if not is_ground else default_shape_large_stair1 "s2": item_shapes = default_shape_normal_stair2 if not is_ground else default_shape_large_stair2 _: item_shapes = [mesh.create_convex_shape(), Transform3D.IDENTITY] if item_shapes: mesh_library.set_item_shapes(mesh_id, item_shapes) _save_mesh_library(mesh_library_level, mesh_library_level_id_list, with_clear) _save_mesh_library(mesh_library_ground, mesh_library_ground_id_list, with_clear) func _save_mesh_library(mesh_library: MeshLibrary, id_list: Array, with_clear: bool): if with_clear: var ids: PackedInt32Array = mesh_library.get_item_list() for id in ids: if not id in id_list: mesh_library.remove_item(id) var meshes: Array[Variant] = [] for id in id_list: meshes.append(mesh_library.get_item_mesh(id)) var previews: Array[Texture2D] = EditorInterface.make_mesh_previews(meshes, 64) for i in range(len(id_list)): mesh_library.set_item_preview(id_list[i], previews[i]) ResourceSaver.save(mesh_library) func refresh_all_animation_by_sprite_frames(sprite_frames: SpriteFrames): var dir_path: String = "res://resource/skill_animation" var dir: DirAccess = DirAccess.open(dir_path) var sprite_frames_name: String = sprite_frames.resource_path.get_file().split('-')[0] for file in dir.get_files(): var path: String = dir_path + "/" + file var res = load(path) if not res is Animation: continue var animation: Animation = res as Animation var sprite_frames_track: int = animation.find_track(NodePath("View:sprite_frames"), Animation.TYPE_VALUE) if sprite_frames_track < 0: continue var sprite_frames_track_key_count: int = animation.track_get_key_count(sprite_frames_track) if sprite_frames_track_key_count == 0: continue var sprite_frames_value: SpriteFrames = animation.track_get_key_value(sprite_frames_track, 0) as SpriteFrames var target_sprite_frames_name: String = sprite_frames_value.resource_path.get_file().split('-')[0] if sprite_frames_name != target_sprite_frames_name: continue var animation_track: int = animation.find_track(NodePath("View:animation"), Animation.TYPE_VALUE) if animation_track < 0: continue var animation_track_key_count: int = animation.track_get_key_count(animation_track) if animation_track_key_count == 0: continue var animation_value: String = animation.track_get_key_value(animation_track, 0) as String refresh_animation_by_sprite_frames(path, sprite_frames, animation_value, animation) Util.refresh_animation_lib() func refresh_animation_by_sprite_frames(animation_path: String, sprite_frames: SpriteFrames, animation_name: String, animation: Animation) -> void: if not sprite_frames.has_animation(animation_name): print("动画不存在: ", animation_name) return var track_sprite_frames: int = animation.find_track(NodePath("View:sprite_frames"), Animation.TYPE_VALUE) if track_sprite_frames < 0: track_sprite_frames = animation.add_track(Animation.TYPE_VALUE) animation.track_set_path(track_sprite_frames, "View:sprite_frames") animation.track_insert_key(track_sprite_frames, 0, sprite_frames) var track_animation: int = animation.find_track(NodePath("View:animation"), Animation.TYPE_VALUE) if track_animation < 0: track_animation = animation.add_track(Animation.TYPE_VALUE) animation.track_set_path(track_animation, "View:animation") animation.track_insert_key(track_animation, 0, animation_name) var track_frame: int = animation.find_track(NodePath("View:frame"), Animation.TYPE_VALUE) if track_frame >= 0: animation.remove_track(track_frame) track_frame = animation.add_track(Animation.TYPE_VALUE) animation.track_set_path(track_frame, "View:frame") var animation_speed: float = Setting.animation_frame_rate var animation_frame_count: int = sprite_frames.get_frame_count(animation_name) animation.length = animation_speed*animation_frame_count for i in range(0, animation_frame_count): var time = i * animation_speed animation.track_insert_key(track_frame, time, i) animation.value_track_set_update_mode(track_sprite_frames, Animation.UPDATE_DISCRETE) animation.value_track_set_update_mode(track_animation, Animation.UPDATE_DISCRETE) animation.value_track_set_update_mode(track_frame, Animation.UPDATE_DISCRETE) print("refresh_animation: ", animation_name, "(", animation_frame_count, " frames)") ResourceSaver.save(animation, animation_path) func get_all_skill_player_weapon() -> Array[SkillWeaponCfg]: var ret: Array[SkillWeaponCfg] = [] var dir_path: String = "res://config/skill_player_weapon" var dir: DirAccess = DirAccess.open(dir_path) for file in dir.get_files(): var path: String = dir_path + "/" + file var res = load(path) if res is SkillWeaponCfg: ret.append(res) return ret func get_all_skill_player_basic() -> Array[SkillWeaponCfg]: var ret: Array[SkillWeaponCfg] = [] var dir_path: String = "res://config/skill_player_basic" var dir: DirAccess = DirAccess.open(dir_path) for file in dir.get_files(): var path: String = dir_path + "/" + file var res = load(path) if res is SkillWeaponCfg: ret.append(res) return ret func get_skill_player_weapon_by_weapon(weapon: WeaponCfg) -> Array[SkillWeaponCfg]: var ret: Array[SkillWeaponCfg] = [] var dir_path: String = "res://config/skill_player_weapon" var dir: DirAccess = DirAccess.open(dir_path) for file in dir.get_files(): var path: String = dir_path + "/" + file var res = load(path) if res is SkillWeaponCfg: if res.weapon == weapon: ret.append(res) return ret