Add bubbles and scout areas.
加入气泡和侦察区域。
This commit is contained in:
parent
4622d06e5c
commit
adee87429e
|
@ -1,9 +1,10 @@
|
||||||
[gd_scene load_steps=9 format=3 uid="uid://cj65pso40syj5"]
|
[gd_scene load_steps=11 format=3 uid="uid://cj65pso40syj5"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/character/AiCharacter.cs" id="1_ubaid"]
|
[ext_resource type="Script" path="res://scripts/character/AiCharacter.cs" id="1_ubaid"]
|
||||||
[ext_resource type="Texture2D" uid="uid://b1twcink38sh0" path="res://sprites/Player.png" id="2_eha68"]
|
[ext_resource type="Texture2D" uid="uid://b1twcink38sh0" path="res://sprites/Player.png" id="2_eha68"]
|
||||||
[ext_resource type="Script" path="res://scripts/damage/DamageNumberNodeSpawn.cs" id="3_kiam3"]
|
[ext_resource type="Script" path="res://scripts/damage/DamageNumberNodeSpawn.cs" id="3_kiam3"]
|
||||||
[ext_resource type="PackedScene" uid="uid://sqqfrmikmk5v" path="res://prefab/ui/HealthBar.tscn" id="4_gt388"]
|
[ext_resource type="PackedScene" uid="uid://sqqfrmikmk5v" path="res://prefab/ui/HealthBar.tscn" id="4_gt388"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/bubble/BubbleMarker.cs" id="5_y2fh5"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_bb8wt"]
|
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_bb8wt"]
|
||||||
radius = 20.0
|
radius = 20.0
|
||||||
|
@ -24,7 +25,10 @@ animations = [{
|
||||||
}]
|
}]
|
||||||
|
|
||||||
[sub_resource type="CircleShape2D" id="CircleShape2D_c61vr"]
|
[sub_resource type="CircleShape2D" id="CircleShape2D_c61vr"]
|
||||||
radius = 129.027
|
radius = 82.2192
|
||||||
|
|
||||||
|
[sub_resource type="CircleShape2D" id="CircleShape2D_fowd5"]
|
||||||
|
radius = 233.808
|
||||||
|
|
||||||
[node name="DelivererOfDarkMagic" type="CharacterBody2D"]
|
[node name="DelivererOfDarkMagic" type="CharacterBody2D"]
|
||||||
collision_layer = 64
|
collision_layer = 64
|
||||||
|
@ -80,3 +84,18 @@ shape = SubResource("CircleShape2D_c61vr")
|
||||||
|
|
||||||
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
|
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
|
||||||
debug_enabled = true
|
debug_enabled = true
|
||||||
|
|
||||||
|
[node name="BubbleMarker" type="Marker2D" parent="."]
|
||||||
|
position = Vector2(0, -79)
|
||||||
|
script = ExtResource("5_y2fh5")
|
||||||
|
|
||||||
|
[node name="VisibleOnScreenEnabler2D" type="VisibleOnScreenEnabler2D" parent="."]
|
||||||
|
position = Vector2(0, 5.5)
|
||||||
|
scale = Vector2(2.04, 3.05)
|
||||||
|
|
||||||
|
[node name="ScoutArea2D" type="Area2D" parent="."]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 68
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ScoutArea2D"]
|
||||||
|
shape = SubResource("CircleShape2D_fowd5")
|
||||||
|
|
|
@ -9,7 +9,6 @@ grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="Label" type="Label" parent="."]
|
||||||
z_index = 1
|
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_right = 40.0
|
offset_right = 40.0
|
||||||
offset_bottom = 23.0
|
offset_bottom = 23.0
|
||||||
|
|
27
prefab/ui/plaint.tscn
Normal file
27
prefab/ui/plaint.tscn
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[gd_scene format=3 uid="uid://dmiyu1y726uo8"]
|
||||||
|
|
||||||
|
[node name="Plaint" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -20.0
|
||||||
|
offset_top = -36.0
|
||||||
|
offset_right = 20.0
|
||||||
|
offset_bottom = 36.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_colors/font_color = Color(0.988235, 0.768627, 0.0980392, 1)
|
||||||
|
theme_override_font_sizes/font_size = 48
|
||||||
|
text = "!"
|
||||||
|
horizontal_alignment = 1
|
26
prefab/ui/query.tscn
Normal file
26
prefab/ui/query.tscn
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[gd_scene format=3 uid="uid://bvpvdloxe4wdf"]
|
||||||
|
|
||||||
|
[node name="Query" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -20.0
|
||||||
|
offset_top = -36.0
|
||||||
|
offset_right = 20.0
|
||||||
|
offset_bottom = 36.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 48
|
||||||
|
text = "?"
|
||||||
|
horizontal_alignment = 1
|
65
scripts/bubble/BubbleMarker.cs
Normal file
65
scripts/bubble/BubbleMarker.cs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ColdMint.scripts.utils;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace ColdMint.scripts.bubble;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>BubbleMarker</para>
|
||||||
|
/// <para>气泡位置标记</para>
|
||||||
|
/// </summary>
|
||||||
|
public partial class BubbleMarker : Marker2D
|
||||||
|
{
|
||||||
|
private readonly Dictionary<int, Node2D> _bubbleDictionary = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Add bubbles</para>
|
||||||
|
/// <para>添加气泡</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool AddBubble(int id, Node2D node)
|
||||||
|
{
|
||||||
|
if (!_bubbleDictionary.TryAdd(id, node))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Hide();
|
||||||
|
NodeUtils.CallDeferredAddChild(this, node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>DisplayBubble</para>
|
||||||
|
/// <para>显示气泡</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Display specific nodes above the creature as "bubbles", for example, question bubbles when an enemy finds the player.</para>
|
||||||
|
///<para>在生物头顶显示特定的节点作为“气泡”,例如:当敌人发现玩家后将显示疑问气泡。</para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
public void ShowBubble(int id)
|
||||||
|
{
|
||||||
|
if (!_bubbleDictionary.TryGetValue(id, out var value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Hidden bubble</para>
|
||||||
|
/// <para>隐藏气泡</para>
|
||||||
|
/// </summary>
|
||||||
|
public void HideBubble(int id)
|
||||||
|
{
|
||||||
|
if (!_bubbleDictionary.TryGetValue(id, out var value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value.Hide();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using ColdMint.scripts.bubble;
|
||||||
using ColdMint.scripts.camp;
|
using ColdMint.scripts.camp;
|
||||||
using ColdMint.scripts.stateMachine;
|
using ColdMint.scripts.stateMachine;
|
||||||
|
using ColdMint.scripts.utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace ColdMint.scripts.character;
|
namespace ColdMint.scripts.character;
|
||||||
|
@ -19,12 +22,28 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
private Vector2 _wallDetectionOrigin;
|
private Vector2 _wallDetectionOrigin;
|
||||||
private Area2D? _attackArea;
|
private Area2D? _attackArea;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Reconnaissance area</para>
|
||||||
|
/// <para>侦察区域</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Most of the time, when the enemy enters the reconnaissance area, the character will issue a "question mark" and try to move slowly towards the event point.</para>
|
||||||
|
///<para>大多数情况下,当敌人进入侦察区域后,角色会发出“疑问(问号)”,并尝试向事件点缓慢移动。</para>
|
||||||
|
/// </remarks>
|
||||||
|
private Area2D? _scoutArea;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>All enemies within striking distance</para>
|
/// <para>All enemies within striking distance</para>
|
||||||
/// <para>在攻击范围内的所有敌人</para>
|
/// <para>在攻击范围内的所有敌人</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<CharacterTemplate>? _enemyInTheAttackRange;
|
private List<CharacterTemplate>? _enemyInTheAttackRange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Scout all enemies within range</para>
|
||||||
|
/// <para>在侦察范围内所有的敌人</para>
|
||||||
|
/// </summary>
|
||||||
|
private List<CharacterTemplate>? _enemyInTheScoutRange;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Obstacle detection ray during attack</para>
|
/// <para>Obstacle detection ray during attack</para>
|
||||||
|
@ -37,6 +56,8 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
private RayCast2D? _attackObstacleDetection;
|
private RayCast2D? _attackObstacleDetection;
|
||||||
|
|
||||||
|
|
||||||
|
private VisibleOnScreenEnabler2D? _screenEnabler2D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Navigation agent</para>
|
/// <para>Navigation agent</para>
|
||||||
/// <para>导航代理</para>
|
/// <para>导航代理</para>
|
||||||
|
@ -49,12 +70,69 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
|
|
||||||
public RayCast2D? AttackObstacleDetection => _attackObstacleDetection;
|
public RayCast2D? AttackObstacleDetection => _attackObstacleDetection;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Exclamation bubble Id</para>
|
||||||
|
/// <para>感叹气泡Id</para>
|
||||||
|
/// </summary>
|
||||||
|
private const int plaintBubbleId = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Query bubble Id</para>
|
||||||
|
/// <para>疑问气泡Id</para>
|
||||||
|
/// </summary>
|
||||||
|
private const int queryBubbleId = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>BubbleMarker</para>
|
||||||
|
/// <para>气泡标记</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Subsequent production of dialogue bubbles can be put into the parent class for players to use.</para>
|
||||||
|
///<para>后续制作对话泡时可进其放到父类,供玩家使用。</para>
|
||||||
|
/// </remarks>
|
||||||
|
private BubbleMarker? _bubbleMarker;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
|
|
||||||
_enemyInTheAttackRange = new List<CharacterTemplate>();
|
_enemyInTheAttackRange = new List<CharacterTemplate>();
|
||||||
|
_enemyInTheScoutRange = new List<CharacterTemplate>();
|
||||||
|
_screenEnabler2D = GetNode<VisibleOnScreenEnabler2D>("VisibleOnScreenEnabler2D");
|
||||||
|
_screenEnabler2D.ScreenEntered += () =>
|
||||||
|
{
|
||||||
|
//When the character enters the screen.
|
||||||
|
//当角色进入屏幕。
|
||||||
|
ProcessMode = ProcessModeEnum.Disabled;
|
||||||
|
};
|
||||||
|
_screenEnabler2D.ScreenExited += () =>
|
||||||
|
{
|
||||||
|
//When the character leaves the screen.
|
||||||
|
//当角色离开屏幕。
|
||||||
|
ProcessMode = ProcessModeEnum.Inherit;
|
||||||
|
};
|
||||||
|
_bubbleMarker = GetNode<BubbleMarker>("BubbleMarker");
|
||||||
|
if (_bubbleMarker != null)
|
||||||
|
{
|
||||||
|
using var plaintScene = GD.Load<PackedScene>("res://prefab/ui/plaint.tscn");
|
||||||
|
var plaint = NodeUtils.InstantiatePackedScene<Node2D>(plaintScene);
|
||||||
|
if (plaint != null)
|
||||||
|
{
|
||||||
|
_bubbleMarker.AddBubble(plaintBubbleId, plaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queryScene = GD.Load<PackedScene>("res://prefab/ui/query.tscn");
|
||||||
|
var query = NodeUtils.InstantiatePackedScene<Node2D>(queryScene);
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
_bubbleMarker.AddBubble(queryBubbleId, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_wallDetection = GetNode<RayCast2D>("WallDetection");
|
_wallDetection = GetNode<RayCast2D>("WallDetection");
|
||||||
_attackArea = GetNode<Area2D>("AttackArea2D");
|
_attackArea = GetNode<Area2D>("AttackArea2D");
|
||||||
|
_scoutArea = GetNode<Area2D>("ScoutArea2D");
|
||||||
NavigationAgent2D = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
NavigationAgent2D = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
||||||
if (ItemMarker2D != null)
|
if (ItemMarker2D != null)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +151,14 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
_attackArea.BodyExited += ExitTheAttackArea;
|
_attackArea.BodyExited += ExitTheAttackArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_scoutArea != null)
|
||||||
|
{
|
||||||
|
_scoutArea.Monitoring = true;
|
||||||
|
_scoutArea.Monitorable = false;
|
||||||
|
_scoutArea.BodyEntered += EnterTheScoutArea;
|
||||||
|
_scoutArea.BodyExited += ExitTheScoutArea;
|
||||||
|
}
|
||||||
|
|
||||||
_wallDetectionOrigin = _wallDetection.TargetPosition;
|
_wallDetectionOrigin = _wallDetection.TargetPosition;
|
||||||
StateMachine = new PatrolStateMachine();
|
StateMachine = new PatrolStateMachine();
|
||||||
StateMachine.Context = new StateContext
|
StateMachine.Context = new StateContext
|
||||||
|
@ -87,34 +173,78 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>EnemyDetected</para>
|
/// <para>Display exclamation marks</para>
|
||||||
/// <para>是否发现敌人</para>
|
/// <para>显示感叹号</para>
|
||||||
|
/// </summary>
|
||||||
|
public void DispladyPlaint()
|
||||||
|
{
|
||||||
|
_bubbleMarker?.ShowBubble(plaintBubbleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HidePlaint()
|
||||||
|
{
|
||||||
|
_bubbleMarker?.HideBubble(plaintBubbleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Displady Query</para>
|
||||||
|
/// <para>显示疑问</para>
|
||||||
|
/// </summary>
|
||||||
|
public void DispladyQuery()
|
||||||
|
{
|
||||||
|
_bubbleMarker?.ShowBubble(queryBubbleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HiddenQuery()
|
||||||
|
{
|
||||||
|
_bubbleMarker?.HideBubble(queryBubbleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Whether the enemy has been detected in the reconnaissance area</para>
|
||||||
|
/// <para>侦察范围是否发现敌人</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
///<para>Have you spotted the enemy?</para>
|
///<para>Have you spotted the enemy?</para>
|
||||||
///<para>是否发现敌人</para>
|
///<para>是否发现敌人</para>
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool EnemyDetected()
|
public bool ScoutEnemyDetected()
|
||||||
{
|
{
|
||||||
if (_enemyInTheAttackRange == null)
|
if (_enemyInTheScoutRange == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _enemyInTheAttackRange.Count > 0;
|
return _enemyInTheScoutRange.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>Get the first enemy to enter range</para>
|
/// <para>Get the first enemy in range</para>
|
||||||
/// <para>获取第一个进入范围的敌人</para>
|
/// <para>获取第一个进入侦察范围的敌人</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public CharacterTemplate? GetFirstEnemy()
|
public CharacterTemplate? GetFirstEnemyInScoutArea()
|
||||||
|
{
|
||||||
|
if (_enemyInTheScoutRange == null || _enemyInTheScoutRange.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _enemyInTheScoutRange[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Get the first enemy within striking range</para>
|
||||||
|
/// <para>获取第一个进入攻击范围的敌人</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public CharacterTemplate? GetFirstEnemyInAttackArea()
|
||||||
{
|
{
|
||||||
if (_enemyInTheAttackRange == null || _enemyInTheAttackRange.Count == 0)
|
if (_enemyInTheAttackRange == null || _enemyInTheAttackRange.Count == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _enemyInTheAttackRange[0];
|
return _enemyInTheAttackRange[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,36 +255,95 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
{
|
{
|
||||||
var nextPathPosition = NavigationAgent2D.GetNextPathPosition();
|
var nextPathPosition = NavigationAgent2D.GetNextPathPosition();
|
||||||
var direction = (nextPathPosition - GlobalPosition).Normalized();
|
var direction = (nextPathPosition - GlobalPosition).Normalized();
|
||||||
velocity = direction * Config.CellSize * Speed;
|
velocity = direction * Config.CellSize * Speed * ProtectedSpeedScale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnterTheAttackArea(Node node)
|
/// <summary>
|
||||||
|
/// <para>When the node enters the reconnaissance area</para>
|
||||||
|
/// <para>当节点进入侦察区域后</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
private void EnterTheScoutArea(Node node)
|
||||||
|
{
|
||||||
|
CanCauseHarmNode(node, (canCause, characterTemplate) =>
|
||||||
|
{
|
||||||
|
if (canCause && characterTemplate != null)
|
||||||
|
{
|
||||||
|
_enemyInTheScoutRange?.Add(characterTemplate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>When the node exits the reconnaissance area</para>
|
||||||
|
/// <para>当节点退出侦察区域后</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
private void ExitTheScoutArea(Node node)
|
||||||
{
|
{
|
||||||
if (node == this)
|
if (node == this)
|
||||||
{
|
{
|
||||||
//The target can't be yourself.
|
|
||||||
//攻击目标不能是自己。
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node is CharacterTemplate characterTemplate)
|
if (node is CharacterTemplate characterTemplate)
|
||||||
{
|
{
|
||||||
//Determine if damage can be done between factions
|
_enemyInTheScoutRange?.Remove(characterTemplate);
|
||||||
//判断阵营间是否可造成伤害
|
|
||||||
var camp = CampManager.GetCamp(CampId);
|
|
||||||
var enemyCamp = CampManager.GetCamp(characterTemplate.CampId);
|
|
||||||
if (enemyCamp != null && camp != null)
|
|
||||||
{
|
|
||||||
var canCause = CampManager.CanCauseHarm(camp, enemyCamp);
|
|
||||||
if (canCause)
|
|
||||||
{
|
|
||||||
_enemyInTheAttackRange?.Add(characterTemplate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>When a node enters the attack zone</para>
|
||||||
|
/// <para>当节点进入攻击区域后</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
private void EnterTheAttackArea(Node node)
|
||||||
|
{
|
||||||
|
CanCauseHarmNode(node, (canCause, characterTemplate) =>
|
||||||
|
{
|
||||||
|
if (canCause && characterTemplate != null)
|
||||||
|
{
|
||||||
|
_enemyInTheAttackRange?.Add(characterTemplate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>CanCauseHarmNode</para>
|
||||||
|
/// <para>是否可伤害某个节点</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
private void CanCauseHarmNode(Node node, Action<bool, CharacterTemplate?> action)
|
||||||
|
{
|
||||||
|
if (node == this)
|
||||||
|
{
|
||||||
|
//The target can't be yourself.
|
||||||
|
//攻击目标不能是自己。
|
||||||
|
action.Invoke(false, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node is not CharacterTemplate characterTemplate)
|
||||||
|
{
|
||||||
|
action.Invoke(false, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Determine if damage can be done between factions
|
||||||
|
//判断阵营间是否可造成伤害
|
||||||
|
var camp = CampManager.GetCamp(CampId);
|
||||||
|
var enemyCamp = CampManager.GetCamp(characterTemplate.CampId);
|
||||||
|
if (enemyCamp != null && camp != null)
|
||||||
|
{
|
||||||
|
action.Invoke(CampManager.CanCauseHarm(camp, enemyCamp), characterTemplate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
action.Invoke(false, characterTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
private void ExitTheAttackArea(Node node)
|
private void ExitTheAttackArea(Node node)
|
||||||
{
|
{
|
||||||
if (node == this)
|
if (node == this)
|
||||||
|
@ -194,6 +383,12 @@ public sealed partial class AiCharacter : CharacterTemplate
|
||||||
_attackArea.BodyExited -= ExitTheAttackArea;
|
_attackArea.BodyExited -= ExitTheAttackArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_scoutArea != null)
|
||||||
|
{
|
||||||
|
_scoutArea.BodyEntered -= EnterTheScoutArea;
|
||||||
|
_scoutArea.BodyExited -= ExitTheScoutArea;
|
||||||
|
}
|
||||||
|
|
||||||
if (StateMachine != null)
|
if (StateMachine != null)
|
||||||
{
|
{
|
||||||
StateMachine.Stop();
|
StateMachine.Stop();
|
||||||
|
|
|
@ -38,6 +38,36 @@ public partial class CharacterTemplate : CharacterBody2D
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected const float Speed = 5f;
|
protected const float Speed = 5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Speed multiplier</para>
|
||||||
|
/// <para>速度乘数</para>
|
||||||
|
/// </summary>
|
||||||
|
protected float ProtectedSpeedScale = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Speed multiplier</para>
|
||||||
|
/// <para>速度乘数</para>
|
||||||
|
/// <remarks>
|
||||||
|
///<para>Set to 0.5 to move at 50% of the normal speed.</para>
|
||||||
|
///<para>设置为0.5则以正常速度的50%移动。</para>
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
public float SpeedScale
|
||||||
|
{
|
||||||
|
get => ProtectedSpeedScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value > 1)
|
||||||
|
{
|
||||||
|
ProtectedSpeedScale = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProtectedSpeedScale = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected const float JumpVelocity = -240;
|
protected const float JumpVelocity = -240;
|
||||||
|
|
||||||
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
|
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
|
||||||
|
|
|
@ -210,7 +210,7 @@ public partial class Player : CharacterTemplate
|
||||||
//Moving left and right
|
//Moving left and right
|
||||||
//左右移动
|
//左右移动
|
||||||
var axis = Input.GetAxis("ui_left", "ui_right");
|
var axis = Input.GetAxis("ui_left", "ui_right");
|
||||||
velocity.X = axis * Speed * Config.CellSize;
|
velocity.X = axis * Speed * Config.CellSize * ProtectedSpeedScale;
|
||||||
|
|
||||||
//Use items
|
//Use items
|
||||||
//使用物品
|
//使用物品
|
||||||
|
|
|
@ -17,21 +17,24 @@ public class ChaseStateProcessor : StateProcessorTemplate
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the first enemy to enter the attack range.
|
//Get the first enemy to enter the reconnaissance range.
|
||||||
//获取第一次进入攻击范围的敌人。
|
//获取第一次进入侦察范围的敌人。
|
||||||
var enemy = aiCharacter.GetFirstEnemy();
|
var enemy = aiCharacter.GetFirstEnemyInScoutArea();
|
||||||
if (enemy == null)
|
if (enemy == null)
|
||||||
{
|
{
|
||||||
//No more enemies. Return to previous status.
|
//No more enemies. Return to previous status.
|
||||||
//没有敌人了,返回上一个状态。
|
//没有敌人了,返回上一个状态。
|
||||||
|
aiCharacter.HiddenQuery();
|
||||||
|
aiCharacter.SetTargetPosition(aiCharacter.GlobalPosition);
|
||||||
LogCat.Log("chase_no_enemy", label: LogCat.LogLabel.ChaseStateProcessor);
|
LogCat.Log("chase_no_enemy", label: LogCat.LogLabel.ChaseStateProcessor);
|
||||||
context.CurrentState = context.PreviousState;
|
context.CurrentState = context.PreviousState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Chase the enemy.
|
//Set the position of the enemy entering the range to the position we are going to.
|
||||||
//追击敌人。
|
//将进入范围的敌人位置设置为我们要前往的位置。
|
||||||
aiCharacter.SetTargetPosition(enemy.GlobalPosition);
|
aiCharacter.SetTargetPosition(enemy.GlobalPosition);
|
||||||
|
aiCharacter.DispladyQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,10 @@ public class PatrolStateProcessor : StateProcessorTemplate
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aiCharacter.EnemyDetected())
|
if (aiCharacter.ScoutEnemyDetected())
|
||||||
{
|
{
|
||||||
|
//Seeing that the enemy had entered the reconnaissance area, we gave chase immediately.
|
||||||
|
//发现敌人进入侦察范围,我们立即追击。
|
||||||
context.CurrentState = State.Chase;
|
context.CurrentState = State.Chase;
|
||||||
LogCat.Log("patrol_enemy_detected", label: LogCat.LogLabel.PatrolStateProcessor);
|
LogCat.Log("patrol_enemy_detected", label: LogCat.LogLabel.PatrolStateProcessor);
|
||||||
return;
|
return;
|
||||||
|
@ -50,12 +52,16 @@ public class PatrolStateProcessor : StateProcessorTemplate
|
||||||
|
|
||||||
if (Points == null || Points.Length == 0)
|
if (Points == null || Points.Length == 0)
|
||||||
{
|
{
|
||||||
|
//There are no patrol points.
|
||||||
|
//没有巡逻点。
|
||||||
LogCat.LogError("no_points", label: LogCat.LogLabel.PatrolStateProcessor);
|
LogCat.LogError("no_points", label: LogCat.LogLabel.PatrolStateProcessor);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_originPosition == null)
|
if (_originPosition == null)
|
||||||
{
|
{
|
||||||
|
//If there is no origin, then wait for the character to collide with the ground to set it as the origin.
|
||||||
|
//如果没有原点,那么等待角色与地面碰撞时将其设置为原点。
|
||||||
if (!aiCharacter.IsOnFloor())
|
if (!aiCharacter.IsOnFloor())
|
||||||
{
|
{
|
||||||
LogCat.LogWarning("patrol_not_on_floor", LogCat.LogLabel.PatrolStateProcessor);
|
LogCat.LogWarning("patrol_not_on_floor", LogCat.LogLabel.PatrolStateProcessor);
|
||||||
|
@ -69,8 +75,10 @@ public class PatrolStateProcessor : StateProcessorTemplate
|
||||||
|
|
||||||
var point = _originPosition + Points[_index];
|
var point = _originPosition + Points[_index];
|
||||||
var distance = aiCharacter.GlobalPosition.DistanceTo(point.Value);
|
var distance = aiCharacter.GlobalPosition.DistanceTo(point.Value);
|
||||||
if (distance < 10)
|
if (distance < 5)
|
||||||
{
|
{
|
||||||
|
//No need to actually come to the patrol point, we just need a distance to get close.
|
||||||
|
//无需真正的来到巡逻点,我们只需要一个距离接近了就可以了。
|
||||||
LogCat.LogWithFormat("patrol_arrival_point", LogCat.LogLabel.PatrolStateProcessor, point);
|
LogCat.LogWithFormat("patrol_arrival_point", LogCat.LogLabel.PatrolStateProcessor, point);
|
||||||
_index++;
|
_index++;
|
||||||
if (_index >= Points.Length)
|
if (_index >= Points.Length)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user