extends CharacterBody2D class_name Unit #这里暂时使用中文键debug,方便调试,后会更改 var action_emoji:Dictionary={ "生气":"🤯", "愤怒":"🤬", "指责":"👈🤯", "伤害":"😡🔪", "友好":"🥰", "被指责":"😟", "受伤":"😭💔", "饥饿":"🍽︎", "好吃":"😋" } #角色数据 @export var unit_data:Dictionary={ } ##背包数据 @export var bag_data:Array=[] #允许的状态队列 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" @export var unit_data_from_id: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_sense_area:Area2D #攻击检测 @export var attack_area:Area2D #攻击模组 var attack_scene:AttackModel #使用数据进行初始化角色 func init_from_data(): Global.set_unit_instance(get_unit_id(),self) agent.max_speed=unit_speed agent.velocity_computed.connect(safe_speed) 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=Vector2(unit_data["sprite_offset"][0],unit_data["sprite_offset"][1]) new_sprite_animation.scale=Vector2(unit_data["sprite_scale"][0],unit_data["sprite_scale"][1]) animation=new_sprite_animation #攻击模式场景 if unit_data.has("attack_scene"): if %rotate!=null: var new_attack_scene=load(unit_data["attack_scene"]).instantiate() %rotate.add_child(new_attack_scene) attack_scene=new_attack_scene Global.unit_instance_dic[get_unit_id()]=self #改变攻击模式 func change_attack(): pass #var hungry:float=0: #set(val): #hungry=val #if %hungry!=null: #%hungry.text="饥饿值:"+str(val) func _ready() -> void: init_from_data() pass 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_sense_area==null: #return false #else: #if instance is PhysicsBody2D: #return instance in attack_sense_area.get_overlapping_bodies() #if instance is Area2D: #return instance in attack_sense_area.get_overlapping_areas() # #pass if attack_scene==null: return false else: return attack_scene.is_unit_instance_in_attack_area(instance) #指责(口角) func accuse(unit_id:String): show_action("指责") var instance=Global.get_unit_instance(unit_id) if instance is UnitOther: instance.accused(self.get_unit_id()) pass #被指责,调用 func accused(by_unit_id:String): show_action("被指责") Global.set_unit_favour(get_unit_id(),by_unit_id,Global.get_unit_favour(get_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 if attack_scene: print("攻击") attack_scene.attack() #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 if attack_scene is AttackModel: return attack_scene.is_attack_finished() else: return true func is_attacking(): #return animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and animation.is_playing() if attack_scene is AttackModel: return attack_scene.is_attacking() else: return false ##在固定帧使用攻击 #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 attack_reset(): if attack_scene: attack_scene.attack_reset() func attacked(by_unit_id:String,damage:float): show_action("受伤") Global.set_unit_favour(get_unit_id(),by_unit_id,Global.get_unit_favour(get_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)) unit_data["position"]=[self.global_position.x,self.global_position.y] unit_data["map"]=Global.now_map_id 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(get_unit_id()) queue_free() pass