extends CharacterBody2D class_name Unit #这里暂时使用中文键debug,方便调试,后会更改 var action_emoji:Dictionary={ "生气":"🤯", "愤怒":"🤬", "指责":"👈🤯", "伤害":"😡🔪", "友好":"🥰", "被指责":"😟", "受伤":"😭💔", "饥饿":"🍽︎", "好吃":"😋" } @export var unit_data:Dictionary={ "unit_id":"default", "unit_type":"default", #动画资源 "sprite_frames":"res://res/animation/other_character_default.tres", #动画偏移 "sprite_offset":Vector2(0,-80), #动画缩放 "sprite_scale":Vector2(5,5), #基础属性 "hp_base":100, "atk_base":10, "speed_base":1, #攻击方式 "attack_type":"base", #下面是临时生成的动态数据 #状态值 #饥饿,疲劳,怒气,紧张,恐慌,压力 "hungry":0, "fatigue":0, "rage":0, "tensity":0, "panic":0, #压力 "pressure":0, "hp_max":100, "hp":100, "atk":10, } #允许的状态队列 var state_value_array:Array=["hungry","fatigue","rage","tensity","panic","pressure","hp_max","hp","atk"] #设置状态值 func get_state_value(state_value_name:String): if state_value_name in state_value_array: return unit_data[state_value_name] else: return null #状态值改变时发出信号 signal state_value_changed(state_value_name:String,value) #设置状态值 func set_state_value(state_value_name:String,value)->bool: if state_value_name in state_value_array: #对每种属性更改进行特殊设置 match state_value_name: "hp": if value<=0: dead() unit_data[state_value_name]=value state_value_changed.emit(state_value_name,value) return true else: return false #获取单位ID func get_unit_id(): return unit_data["unit_id"] #获取单位类型 func get_unit_type(): return unit_data["unit_type"] #快捷获取 func get_hungry(): return get_state_value("hungry") func set_hungry(value): return set_state_value("hungry",value) func get_atk(): return get_state_value("atk") func set_atk(value): return set_state_value("atk",value) ##单位的独特ID @export var unit_id:String="default" ##单位所属族群 @export var unit_type:String="default" ##每秒触发的timer,用于计算饥饿值积累等 var second_timer:Timer @export var animation:AnimatedSprite2D const unit_speed = 300.0 const JUMP_VELOCITY = -400.0 @export var agent: NavigationAgent2D @export var state_machine: StateMachine #旋转中心轴 @export var rotate: Node2D ##与其他单位交互的范围,目标进入此范围内才进行交互 @export var touch_area:Area2D ##感知范围 @export var sense_area:Area2D @export var attack_area:Area2D #var hungry:float=0: #set(val): #hungry=val #if %hungry!=null: #%hungry.text="饥饿值:"+str(val) func _ready() -> void: Global.set_unit_instance(unit_id,self) agent.max_speed=unit_speed agent.velocity_computed.connect(safe_speed) if animation!=null: animation.frame_changed.connect(frame_changed) second_timer=Timer.new() second_timer.autostart=true second_timer.one_shot=false second_timer.wait_time=1 second_timer.timeout.connect(second_timer_time_out) add_child(second_timer) var new_sprite_animation:AnimatedSprite2D=AnimatedSprite2D.new() new_sprite_animation.sprite_frames=load(unit_data["sprite_frames"]) add_child(new_sprite_animation) new_sprite_animation.position=unit_data["sprite_offset"] new_sprite_animation.scale=unit_data["sprite_scale"] animation=new_sprite_animation animation.frame_changed.connect(frame_changed) #state_machine.launch() func set_target_pos(target:Vector2): agent.target_position=target func _physics_process(delta: float) -> void: if agent!=null and not is_move_finished(): var current_pos:Vector2=global_position var next_pos:Vector2=agent.get_next_path_position() var new_velocity=current_pos.direction_to(next_pos)*unit_speed if agent.avoidance_enabled: agent.set_velocity(new_velocity) rotate.rotation=velocity.angle() else: safe_speed(new_velocity) rotate.rotation=velocity.angle() if is_move_finished(): velocity=Vector2.ZERO else: cache_velicity=velocity if animation!=null: if velocity.x>0: animation.flip_h=false if velocity.x<0: animation.flip_h=true move_and_slide() func safe_speed(safe_velocity): velocity=safe_velocity func stop_move(): set_target_pos(self.global_position) func is_move_finished(): #return agent.is_target_reached() return agent.is_navigation_finished() func sent_message(type:String,value): state_machine.send_message(type,value) #判断unit实例在不在交互范围内 func is_unit_instance_in_touch_area(instance:Node): if touch_area==null: return false else: if instance is PhysicsBody2D: return instance in touch_area.get_overlapping_bodies() if instance is Area2D: return instance in touch_area.get_overlapping_areas() #判断单位是否再攻击范围内 func is_unit_instance_in_attack_area(instance:Node): if attack_area==null: return false else: if instance is PhysicsBody2D: return instance in attack_area.get_overlapping_bodies() if instance is Area2D: return instance in attack_area.get_overlapping_areas() pass #指责(口角) func accuse(unit_id:String): show_action("指责") var instance=Global.get_unit_instance(unit_id) if instance is UnitOther: instance.accused(self.unit_id) pass #被指责,调用 func accused(by_unit_id:String): show_action("被指责") Global.set_unit_favour(unit_id,by_unit_id,Global.get_unit_favour(unit_id,by_unit_id)-10) pass @export var attack_frames:int=2 #攻击 func attack(): show_action("伤害") match get_dir(): 0: play_animation("up_attack") 1: play_animation("down_attack") 2: play_animation("left_right_attack") 3: play_animation("left_right_attack") pass func frame_changed(): if animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and animation.frame==attack_frames: use_attack_damage() func is_attack_finished(): if animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and not animation.is_playing(): return true else: return false func is_attacking(): return animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and animation.is_playing() #在固定帧使用攻击 func use_attack_damage(): print("使用攻击") for i in attack_area.get_overlapping_bodies(): if i is Unit and i!=self: i.attacked(unit_id,get_atk()) pass func attacked(by_unit_id:String,damage:float): show_action("受伤") Global.set_unit_favour(unit_id,by_unit_id,Global.get_unit_favour(unit_id,by_unit_id)-20) set_state_value("hp",get_state_value("hp")-damage) pass func set_target(global_pos:Vector2): cache_velicity=global_pos-self.global_position rotate.look_at(global_pos) if cache_velicity.x>0: animation.flip_h=false if cache_velicity.x<0: animation.flip_h=true pass func show_action(type:String): print(type) if action_emoji.has(type): %action_show.text=action_emoji[type] if %action_animation.is_playing(): %action_animation.stop() %action_animation.play("show") pass func play_animation(animation_name:String): if animation !=null: animation.play(animation_name) pass var cache_velicity:Vector2=Vector2(1,0) #0上,1下,2左,3右 func get_dir()->int: if abs(cache_velicity.y)>abs(cache_velicity.x): if cache_velicity.y>0: return 1 else: return 0 else: if cache_velicity.x>0: return 3 else: return 2 pass pass func second_timer_time_out(): set_hungry(clamp(get_hungry()+1,0,100)) pass func eat(food:Food): print("吃") show_action("好吃") set_hungry(clamp(get_hungry()-food.hungry,0,100)) food.eated() pass const DEAD_SCENE = preload("res://scene/character/dead_scene_sprite/dead_scene.tscn") func dead(): var new_dead_scene=DEAD_SCENE.instantiate() new_dead_scene.sprite_frames=animation.sprite_frames new_dead_scene.global_position=animation.global_position new_dead_scene.scale=animation.scale get_parent().add_child(new_dead_scene) Global.delete_unit_instance(unit_id) queue_free() pass