(client) feat:实现热重载,实现多维度,实现武器,实现掉落物,实现状态UI,实现攻击AI #44
File diff suppressed because it is too large
Load Diff
@ -6,9 +6,6 @@ namespace CameraControl
|
||||
{
|
||||
public class CameraControl : Utils.MonoSingleton<CameraControl>, ITick, ITickUI
|
||||
{
|
||||
// 当前被聚焦的目标实体
|
||||
public Entity.Entity focusedEntity = null;
|
||||
|
||||
// Camera movement variables
|
||||
private Vector3 _dragOrigin;
|
||||
private bool _isDragging = false;
|
||||
@ -50,12 +47,12 @@ namespace CameraControl
|
||||
}
|
||||
public void Tick()
|
||||
{
|
||||
if (focusedEntity)
|
||||
if (Program.Instance.focusedEntity)
|
||||
{
|
||||
// Follow the focused entity's position
|
||||
var targetPosition = new Vector3(
|
||||
focusedEntity.Position.x,
|
||||
focusedEntity.Position.y,
|
||||
Program.Instance.focusedEntity.Position.x,
|
||||
Program.Instance.focusedEntity.Position.y,
|
||||
_camera.transform.position.z);
|
||||
|
||||
_camera.transform.position = Vector3.Lerp(
|
||||
@ -86,7 +83,8 @@ namespace CameraControl
|
||||
{
|
||||
_dragOrigin = _camera.ScreenToWorldPoint(Input.mousePosition);
|
||||
_isDragging = true;
|
||||
focusedEntity = null; // Clear focus when manually moving camera
|
||||
Program.Instance.focusedEntity.PlayerControlled = false;
|
||||
Program.Instance.focusedEntity = null; // Clear focus when manually moving camera
|
||||
}
|
||||
|
||||
// During drag
|
||||
|
@ -271,17 +271,17 @@ namespace Configs
|
||||
public static Dictionary<string, T> LoadResources<T>(string path) where T : UnityEngine.Object
|
||||
{
|
||||
// 创建一个字典来存储资源名称和加载好的资源
|
||||
Dictionary<string, T> resourceDict = new Dictionary<string, T>();
|
||||
var resourceDict = new Dictionary<string, T>();
|
||||
|
||||
// 加载指定路径下的所有资源
|
||||
T[] resources = Resources.LoadAll<T>(path);
|
||||
var resources = Resources.LoadAll<T>(path);
|
||||
|
||||
foreach (T resource in resources)
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
if (resource != null)
|
||||
{
|
||||
// 获取资源名称并存入字典
|
||||
string resourceName = resource.name;
|
||||
var resourceName = resource.name;
|
||||
resourceDict[resourceName] = resource;
|
||||
}
|
||||
}
|
||||
@ -314,7 +314,7 @@ namespace Configs
|
||||
try
|
||||
{
|
||||
// 使用 using 自动管理 FileStream 的生命周期
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
fs.Seek(0, SeekOrigin.Begin); // 将游标移动到文件开头(可选)
|
||||
bytes = new byte[fs.Length]; // 创建一个字节数组来存储文件内容
|
||||
@ -328,10 +328,14 @@ namespace Configs
|
||||
}
|
||||
|
||||
// 创建一个默认大小的 Texture2D 对象
|
||||
Texture2D texture = new Texture2D(2, 2); // 初始大小为 2x2,LoadImage 会自动调整大小
|
||||
var texture = new Texture2D(2, 2, TextureFormat.RGBA32, false); // 初始大小为 2x2,LoadImage 会自动调整大小
|
||||
|
||||
if (texture.LoadImage(bytes)) // 加载图片数据
|
||||
{
|
||||
// 设置过滤模式为 Point (无插值)
|
||||
texture.filterMode = FilterMode.Point;
|
||||
texture.wrapMode = TextureWrapMode.Clamp; // 防止边缘出现意外拉伸
|
||||
|
||||
return texture; // 返回加载成功的 Texture2D 对象
|
||||
}
|
||||
else
|
||||
|
@ -227,7 +227,7 @@ namespace Data
|
||||
|
||||
public class DrawNodeDef : Define
|
||||
{
|
||||
public List<string> animationTextures = new();
|
||||
public List<string> textures = new();
|
||||
public List<DrawNodeDef> nodes = new();
|
||||
public string nodeName;
|
||||
public Vector2 position = new(0, 0);
|
||||
|
@ -54,10 +54,8 @@ namespace Entity
|
||||
return;
|
||||
|
||||
// 计算当前向上方向与目标方向之间的角度
|
||||
float angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
|
||||
|
||||
// 调整角度,因为默认贴图向上是0度,而Atan2计算的是相对于x轴的角度
|
||||
angle -= 90f;
|
||||
var angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
|
||||
|
||||
|
||||
// 应用旋转
|
||||
transform.rotation = Quaternion.Euler(0f, 0f, angle);
|
||||
|
@ -84,12 +84,20 @@ namespace Entity
|
||||
{
|
||||
IsChase = true;
|
||||
currentJob = null;
|
||||
if(Program.Instance.focusedEntity)
|
||||
Program.Instance.focusedEntity.PlayerControlled = false;
|
||||
Program.Instance.focusedEntity = this;
|
||||
}
|
||||
else if (PlayerControlled)
|
||||
{
|
||||
Program.Instance.focusedEntity = null;
|
||||
}
|
||||
_isPlayerControlled = value;
|
||||
}
|
||||
get => _isPlayerControlled;
|
||||
get => Program.Instance.focusedEntity == this;
|
||||
}
|
||||
|
||||
public bool IsWalking => _walkingTimer > 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取实体当前位置。
|
||||
/// </summary>
|
||||
@ -99,18 +107,16 @@ namespace Entity
|
||||
/// 表示实体是否已经死亡(生命值小于等于零)。
|
||||
/// </summary>
|
||||
public bool IsDead => attributes.health <= 0;
|
||||
public bool IsShowingHealthBarUI=>hitBarUIShowTimer > 0;
|
||||
public bool IsAttacking => attackCoroutine != null;
|
||||
public bool IsShowingHealthBarUI=>_hitBarUIShowTimer > 0;
|
||||
public bool IsAttacking => _attackCoroutine != null;
|
||||
|
||||
|
||||
private bool _isPlayerControlled = false;
|
||||
private bool _warning = false;
|
||||
|
||||
/// <summary>
|
||||
/// 存储不同朝向下的动画节点集合。
|
||||
/// </summary>
|
||||
public Dictionary<EntityState, Dictionary<Orientation, List<ITick>>> bodyAnimationNode = new();
|
||||
private List<ITick> currentAnimatorCache=new ();
|
||||
private List<ITick> _currentAnimatorCache=new ();
|
||||
/// <summary>
|
||||
/// 存储不同朝向下的身体节点对象。
|
||||
/// </summary>
|
||||
@ -119,30 +125,31 @@ namespace Entity
|
||||
/// <summary>
|
||||
/// 当前实体的朝向。
|
||||
/// </summary>
|
||||
private Orientation currentOrientation = Orientation.Down;
|
||||
private Orientation _currentOrientation = Orientation.Down;
|
||||
/// <summary>
|
||||
/// 当前实体的状态
|
||||
/// </summary>
|
||||
private EntityState currentState = EntityState.Idle;
|
||||
private EntityState _currentState = EntityState.Idle;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 攻击动画的持续时间(秒)。
|
||||
/// </summary>
|
||||
private const float attackAnimationDuration = 0.1f;
|
||||
private const float AttackAnimationDuration = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// 抖动的偏移量。
|
||||
/// </summary>
|
||||
private const float shakeOffset = 0.5f;
|
||||
private const float ShakeOffset = 0.5f;
|
||||
// 协程引用
|
||||
private Coroutine attackCoroutine;
|
||||
private Coroutine _attackCoroutine;
|
||||
|
||||
protected EntityDef entityDef;
|
||||
|
||||
|
||||
public float hitBarUIShowTime = 5;
|
||||
private float hitBarUIShowTimer = 0;
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
private float _hitBarUIShowTimer = 0;
|
||||
private int _walkingTimer = 1;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -258,7 +265,7 @@ namespace Entity
|
||||
|
||||
GameObject nodeObject = null;
|
||||
// 根据纹理数量创建不同类型的节点
|
||||
switch (drawNode.animationTextures?.Count ?? 0)
|
||||
switch (drawNode.textures?.Count ?? 0)
|
||||
{
|
||||
case 0:
|
||||
// 无纹理节点
|
||||
@ -277,12 +284,12 @@ namespace Entity
|
||||
nodeObject = Instantiate(imagePrefab.gameObject, parent.transform);
|
||||
var texture =
|
||||
Managers.PackagesImageManager.Instance?.GetSprite(drawNode.packID,
|
||||
drawNode.animationTextures[0]);
|
||||
drawNode.textures[0]);
|
||||
|
||||
if (!texture)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"InitBodyPart: 无法获取纹理 (节点名: {drawNode.nodeName}, 纹理ID: {drawNode.animationTextures[0]})");
|
||||
$"InitBodyPart: 无法获取纹理 (节点名: {drawNode.nodeName}, 纹理ID: {drawNode.textures[0]})");
|
||||
}
|
||||
|
||||
var imagePrefabCom = nodeObject.GetComponent<ImagePrefab>();
|
||||
@ -316,7 +323,7 @@ namespace Entity
|
||||
|
||||
animator.SetFPS(drawNode.FPS);
|
||||
var animatedSprites = new List<Sprite>();
|
||||
foreach (var textureId in drawNode.animationTextures)
|
||||
foreach (var textureId in drawNode.textures)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -386,7 +393,15 @@ namespace Entity
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
if (_isPlayerControlled)
|
||||
if (_walkingTimer > 0)
|
||||
{
|
||||
_walkingTimer -= 1;
|
||||
if (_walkingTimer <= 0)
|
||||
{
|
||||
SetBodyTexture(EntityState.Idle, _currentOrientation);
|
||||
}
|
||||
}
|
||||
if (PlayerControlled)
|
||||
{
|
||||
UpdatePlayerControls();
|
||||
}
|
||||
@ -395,9 +410,9 @@ namespace Entity
|
||||
AutoBehave();
|
||||
}
|
||||
|
||||
if (currentAnimatorCache!=null)
|
||||
if (_currentAnimatorCache!=null)
|
||||
{
|
||||
foreach (var animator in currentAnimatorCache)
|
||||
foreach (var animator in _currentAnimatorCache)
|
||||
{
|
||||
animator.Tick();
|
||||
}
|
||||
@ -406,8 +421,8 @@ namespace Entity
|
||||
|
||||
if (IsShowingHealthBarUI)
|
||||
{
|
||||
hitBarUIShowTimer -= Time.deltaTime;
|
||||
if (hitBarUIShowTimer <= 0)
|
||||
_hitBarUIShowTimer -= Time.deltaTime;
|
||||
if (_hitBarUIShowTimer <= 0)
|
||||
{
|
||||
HideHealthBar();
|
||||
}
|
||||
@ -420,14 +435,14 @@ namespace Entity
|
||||
public virtual void TryAttack()
|
||||
{
|
||||
if(!IsAttacking)
|
||||
attackCoroutine = StartCoroutine(AttackFlow());
|
||||
_attackCoroutine = StartCoroutine(AttackFlow());
|
||||
}
|
||||
|
||||
public virtual void SetBodyTexture(EntityState state, Orientation orientation)
|
||||
{
|
||||
if (bodyNodes.TryGetValue(currentState, out var stateNode))
|
||||
if (bodyNodes.TryGetValue(_currentState, out var stateNode))
|
||||
{
|
||||
if (stateNode.TryGetValue(currentOrientation, out var node))
|
||||
if (stateNode.TryGetValue(_currentOrientation, out var node))
|
||||
{
|
||||
node.SetActive(false);
|
||||
}
|
||||
@ -441,14 +456,14 @@ namespace Entity
|
||||
}
|
||||
}
|
||||
|
||||
currentState = state;
|
||||
currentOrientation = orientation;
|
||||
_currentState = state;
|
||||
_currentOrientation = orientation;
|
||||
|
||||
if (bodyAnimationNode.TryGetValue(currentState, out var animationNode))
|
||||
if (bodyAnimationNode.TryGetValue(_currentState, out var animationNode))
|
||||
{
|
||||
if (animationNode.TryGetValue(currentOrientation, out var value))
|
||||
if (animationNode.TryGetValue(_currentOrientation, out var value))
|
||||
{
|
||||
currentAnimatorCache=value;
|
||||
_currentAnimatorCache=value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -461,6 +476,8 @@ namespace Entity
|
||||
if (IsAttacking)
|
||||
return;
|
||||
transform.position += direction * (attributes.moveSpeed * Time.deltaTime * (IsChase ? 1 : 0.5f));
|
||||
SetBodyTexture(EntityState.Walking,_currentOrientation);
|
||||
_walkingTimer = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -483,7 +500,7 @@ namespace Entity
|
||||
return;
|
||||
healthBarPrefab.gameObject.SetActive(true);
|
||||
healthBarPrefab.Progress = (float)attributes.health / entityDef.attributes.health;
|
||||
hitBarUIShowTimer=hitBarUIShowTime;
|
||||
_hitBarUIShowTimer=_hitBarUIShowTime;
|
||||
}
|
||||
|
||||
public void HideHealthBar()
|
||||
@ -521,7 +538,7 @@ namespace Entity
|
||||
ori = direction.x > 0 ? Orientation.Right : Orientation.Left;
|
||||
}
|
||||
|
||||
SetBodyTexture(currentState, ori);
|
||||
SetBodyTexture(_currentState, ori);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -608,7 +625,7 @@ namespace Entity
|
||||
// 调用检测并攻击敌人的方法
|
||||
DetectAndAttackEnemies();
|
||||
// 攻击流程结束,清理协程引用
|
||||
attackCoroutine = null;
|
||||
_attackCoroutine = null;
|
||||
}
|
||||
|
||||
|
||||
@ -623,14 +640,14 @@ namespace Entity
|
||||
StartCoroutine(ShakeInDirectionCoroutine());
|
||||
|
||||
// 返回检测敌人的起始时间
|
||||
return attackAnimationDuration;
|
||||
return AttackAnimationDuration;
|
||||
}
|
||||
|
||||
private IEnumerator ShakeInDirectionCoroutine()
|
||||
{
|
||||
var originalPosition = transform.position; // 记录原始位置
|
||||
transform.position += direction * shakeOffset;
|
||||
yield return new WaitForSeconds(attackAnimationDuration);
|
||||
transform.position += direction * ShakeOffset;
|
||||
yield return new WaitForSeconds(AttackAnimationDuration);
|
||||
transform.position = originalPosition;
|
||||
}
|
||||
|
||||
|
@ -110,14 +110,6 @@ namespace Entity
|
||||
result.Add(("结束操控", EndControl));
|
||||
else
|
||||
result.Add(("手动操控", StartControl));
|
||||
if (CameraControl.CameraControl.Instance.focusedEntity == entity)
|
||||
{
|
||||
result.Add(("取消跟随", ()=>CameraControl.CameraControl.Instance.focusedEntity=null));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(("视角跟随", ()=>CameraControl.CameraControl.Instance.focusedEntity=entity));
|
||||
}
|
||||
result.Add(("杀死", () => entity.Kill()));
|
||||
result.Add(("变成笨蛋", BecomeDefault));
|
||||
return result;
|
||||
@ -132,13 +124,11 @@ namespace Entity
|
||||
private void StartControl()
|
||||
{
|
||||
entity.PlayerControlled = true;
|
||||
CameraControl.CameraControl.Instance.focusedEntity=entity;
|
||||
}
|
||||
|
||||
private void EndControl()
|
||||
{
|
||||
entity.PlayerControlled = false;
|
||||
CameraControl.CameraControl.Instance.focusedEntity=null;
|
||||
}
|
||||
}
|
||||
}
|
@ -296,15 +296,19 @@ namespace Managers
|
||||
var textureWidth = texture.width;
|
||||
var textureHeight = texture.height;
|
||||
|
||||
// 如果不分割(rows和cols都为1),直接创建单个Sprite
|
||||
// 首先创建一个未分割的精灵,使用原始名称
|
||||
if (!sprites.ContainsKey(packId))
|
||||
sprites[packId] = new Dictionary<string, Sprite>();
|
||||
|
||||
// 创建未分割的精灵
|
||||
var fullSpriteRect = new Rect(0, 0, textureWidth, textureHeight);
|
||||
var fullSprite = Sprite.Create(texture, fullSpriteRect, new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
fullSprite.name = baseName; // 使用原始名称
|
||||
sprites[packId][baseName] = fullSprite;
|
||||
|
||||
// 如果不分割(rows和cols都为1),直接返回
|
||||
if (rows == 1 && cols == 1)
|
||||
{
|
||||
if (!sprites.ContainsKey(packId))
|
||||
sprites[packId] = new Dictionary<string, Sprite>();
|
||||
|
||||
var spriteRect = new Rect(0, 0, textureWidth, textureHeight);
|
||||
var sprite = Sprite.Create(texture, spriteRect, new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
sprites[packId][baseName] = sprite;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -316,25 +320,25 @@ namespace Managers
|
||||
Debug.LogError("Texture dimensions are not divisible by the specified rows and columns.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sprites.ContainsKey(packId))
|
||||
sprites[packId] = new Dictionary<string, Sprite>();
|
||||
|
||||
// 分割纹理并创建多个精灵
|
||||
for (var row = 0; row < rows; row++)
|
||||
{
|
||||
for (var col = 0; col < cols; col++)
|
||||
{
|
||||
Rect spriteRect = new(col * tileWidth, row * tileHeight, tileWidth, tileHeight);
|
||||
var sprite = Sprite.Create(texture, spriteRect, new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
|
||||
var index = (rows - row - 1) * cols + col;
|
||||
|
||||
var index = (rows - row - 1) * cols + col; // 计算索引
|
||||
var spriteName = $"{baseName}_{index}";
|
||||
sprite.name = spriteName;
|
||||
|
||||
// 添加到字典中
|
||||
sprites[packId][spriteName] = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
packagesImages.Clear();
|
||||
|
@ -4,7 +4,7 @@ using UnityEngine.Tilemaps;
|
||||
|
||||
namespace Map
|
||||
{
|
||||
public class MapGenerator:Utils.MonoSingleton<MapGenerator>
|
||||
public class MapGenerator:MonoBehaviour
|
||||
{
|
||||
public DoubleMap baseLevel;
|
||||
public Tilemap buildLevel;
|
||||
@ -24,10 +24,6 @@ namespace Map
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -3,5 +3,7 @@ using UnityEngine;
|
||||
|
||||
public class Program : Utils.Singleton<Program>
|
||||
{
|
||||
public Entity.Entity focusedEntity = null;
|
||||
public bool needLoad = true;
|
||||
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
|
||||
public static class Pathfinder
|
||||
{
|
||||
public static List<Vector2> FindPath(Entity.Entity entity, Vector2 target, float maxDistance)
|
||||
{
|
||||
Vector2 start = entity.Position;
|
||||
|
||||
// 计算起点和终点所在的瓦片坐标
|
||||
var startTile = GetTileCoord(start);
|
||||
var endTile = GetTileCoord(target);
|
||||
|
||||
// 如果超出最大距离,直接返回直线路径或空路径
|
||||
if (Vector2.Distance(start, target) > maxDistance)
|
||||
{
|
||||
return new List<Vector2> { start, target };
|
||||
}
|
||||
|
||||
// A*算法数据结构
|
||||
var cameFrom = new Dictionary<Vector2Int, Vector2Int>();
|
||||
var gScore = new Dictionary<Vector2Int, float>();
|
||||
var fScore = new Dictionary<Vector2Int, float>();
|
||||
var openSet = new List<Vector2Int>();
|
||||
|
||||
// 初始化
|
||||
gScore[startTile] = 0;
|
||||
fScore[startTile] = Heuristic(startTile, endTile);
|
||||
openSet.Add(startTile);
|
||||
|
||||
var closestNode = startTile;
|
||||
var closestDist = Vector2.Distance(start, target);
|
||||
|
||||
while (openSet.Count > 0)
|
||||
{
|
||||
// 获取fScore最小的节点
|
||||
var current = openSet[0];
|
||||
foreach (var node in openSet)
|
||||
{
|
||||
if (fScore.GetValueOrDefault(node, float.MaxValue) <
|
||||
fScore.GetValueOrDefault(current, float.MaxValue))
|
||||
{
|
||||
current = node;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否到达目标
|
||||
if (current == endTile)
|
||||
{
|
||||
return ReconstructPath(cameFrom, current, start, target);
|
||||
}
|
||||
|
||||
openSet.Remove(current);
|
||||
|
||||
// 检查最大距离限制
|
||||
var currentDist = Vector2.Distance(
|
||||
new Vector2(current.x, current.y),
|
||||
target);
|
||||
|
||||
if (currentDist < closestDist)
|
||||
{
|
||||
closestDist = currentDist;
|
||||
closestNode = current;
|
||||
}
|
||||
|
||||
if (gScore[current] > maxDistance)
|
||||
{
|
||||
return ReconstructPath(cameFrom, closestNode, start, target);
|
||||
}
|
||||
|
||||
// 遍历邻居(8方向)
|
||||
for (var dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (var dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
|
||||
var neighbor = new Vector2Int(current.x + dx, current.y + dy);
|
||||
|
||||
// 跳过不可通行区域
|
||||
if (!Map.MapGenerator.Instance.CanPassThrough(neighbor.x, neighbor.y))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算移动成本
|
||||
var moveCost = GetMovementCost(current, neighbor);
|
||||
var tentativeGScore = gScore[current] + moveCost;
|
||||
|
||||
// 跳过超出最大距离的路径
|
||||
if (tentativeGScore > maxDistance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发现新节点或找到更好路径
|
||||
if (tentativeGScore < gScore.GetValueOrDefault(neighbor, float.MaxValue))
|
||||
{
|
||||
cameFrom[neighbor] = current;
|
||||
gScore[neighbor] = tentativeGScore;
|
||||
fScore[neighbor] = tentativeGScore + Heuristic(neighbor, endTile);
|
||||
|
||||
if (!openSet.Contains(neighbor))
|
||||
{
|
||||
openSet.Add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 无法找到完整路径时返回局部最优解
|
||||
return ReconstructPath(cameFrom, closestNode, start, target);
|
||||
}
|
||||
|
||||
// 获取瓦片坐标(每个瓦片覆盖±0.5范围)
|
||||
private static Vector2Int GetTileCoord(Vector2 position)
|
||||
{
|
||||
return new Vector2Int(
|
||||
Mathf.RoundToInt(position.x),
|
||||
Mathf.RoundToInt(position.y)
|
||||
);
|
||||
}
|
||||
|
||||
// 计算启发式估值(欧几里得距离)
|
||||
private static float Heuristic(Vector2Int a, Vector2Int b)
|
||||
{
|
||||
return Vector2.Distance(
|
||||
new Vector2(a.x, a.y),
|
||||
new Vector2(b.x, b.y)
|
||||
);
|
||||
}
|
||||
|
||||
// 获取移动成本
|
||||
private static float GetMovementCost(Vector2Int from, Vector2Int to)
|
||||
{
|
||||
// 计算基础距离(正交=1,对角=√2)
|
||||
var distance = (from.x == to.x || from.y == to.y) ? 1f : 1.4142f;
|
||||
|
||||
// 应用目标瓦片的速度削减率
|
||||
var costModifier = Map.MapGenerator.Instance.GetTilePassCost(to.x, to.y);
|
||||
|
||||
// 成本 = 距离 × (1 + 速度削减率)
|
||||
return distance * (1 + costModifier);
|
||||
}
|
||||
|
||||
// 重建路径
|
||||
private static List<Vector2> ReconstructPath(
|
||||
Dictionary<Vector2Int, Vector2Int> cameFrom,
|
||||
Vector2Int current,
|
||||
Vector2 start,
|
||||
Vector2 end)
|
||||
{
|
||||
// 构建瓦片路径
|
||||
var tilePath = new List<Vector2Int>();
|
||||
tilePath.Add(current);
|
||||
|
||||
while (cameFrom.ContainsKey(current))
|
||||
{
|
||||
current = cameFrom[current];
|
||||
tilePath.Add(current);
|
||||
}
|
||||
|
||||
tilePath.Reverse();
|
||||
|
||||
// 转换为实际坐标路径
|
||||
var path = new List<Vector2>();
|
||||
path.Add(start); // 添加精确起点
|
||||
|
||||
// 添加路径点(瓦片中心)
|
||||
foreach (var tile in tilePath)
|
||||
{
|
||||
path.Add(new Vector2(tile.x, tile.y));
|
||||
}
|
||||
|
||||
path.Add(end); // 添加精确终点
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 586ed74166c04dae862f096bdc52f63b
|
||||
timeCreated: 1754750432
|
@ -1,5 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Define>
|
||||
|
||||
<ImageDef>
|
||||
<defName>TestGunItem</defName>
|
||||
<path>Resources\Item\TestGun.png</path>
|
||||
</ImageDef>
|
||||
|
||||
<ImageDef>
|
||||
<defName>TestGun</defName>
|
||||
<path>Resources\Item\TestGunUse.png</path>
|
||||
</ImageDef>
|
||||
<ImageDef>
|
||||
<defName>yellowBullet</defName>
|
||||
<path>Resources\Item\YellowBullet_down.png</path>
|
||||
</ImageDef>
|
||||
|
||||
<AttributesDef>
|
||||
<defName>TestGun</defName>
|
||||
<attack>10</attack>
|
||||
@ -26,26 +41,23 @@
|
||||
<moveSpeed>20</moveSpeed>
|
||||
</attributes>
|
||||
<drawingOrder>
|
||||
<texturePath>Resources\Item</texturePath>
|
||||
<pixelsPerUnit>20</pixelsPerUnit>
|
||||
<drawingOrder_down>
|
||||
<node name="YellowBullet"/>
|
||||
</drawingOrder_down>
|
||||
<idle_down>
|
||||
<textures>
|
||||
<li>yellowBullet</li>
|
||||
</textures>
|
||||
</idle_down>
|
||||
<walk_down>
|
||||
<textures>
|
||||
<li>yellowBullet</li>
|
||||
</textures>
|
||||
</walk_down>
|
||||
</drawingOrder>
|
||||
<behaviorTree>
|
||||
<Node className="JobGiver_ContinuousMove"/>
|
||||
</behaviorTree>
|
||||
</BulletDef>
|
||||
|
||||
<ImageDef>
|
||||
<defName>TestGunItem</defName>
|
||||
<path>Resources\Item\TestGun.png</path>
|
||||
</ImageDef>
|
||||
|
||||
<ImageDef>
|
||||
<defName>TestGun</defName>
|
||||
<path>Resources\Item\TestGunUse.png</path>
|
||||
</ImageDef>
|
||||
|
||||
|
||||
|
||||
|
@ -2,11 +2,17 @@
|
||||
<Define>
|
||||
<!-- 贴图部分 -->
|
||||
<ImageDef>
|
||||
<defName>testPawn</defName>
|
||||
<defName>testPawnAnimation</defName>
|
||||
<path>res:Character/Test/Body/idle_down</path>
|
||||
<wCount>5</wCount>
|
||||
<hCount>1</hCount>
|
||||
</ImageDef>
|
||||
<ImageDef>
|
||||
<defName>testPawn</defName>
|
||||
<path>Resources\Character\test.png</path>
|
||||
<wCount>4</wCount>
|
||||
<hCount>4</hCount>
|
||||
</ImageDef>
|
||||
|
||||
<!-- 人物定义 -->
|
||||
<CharacterDef>
|
||||
@ -19,15 +25,93 @@
|
||||
</attributes>
|
||||
<affiliation>player</affiliation>
|
||||
<drawingOrder>
|
||||
<idle_down name="Body" FPS="2">
|
||||
<animationTextures>
|
||||
<!-- Idle states -->
|
||||
<idle_up>
|
||||
<textures>
|
||||
<li>testPawn_0</li>
|
||||
<li>testPawn_1</li>
|
||||
<li>testPawn_2</li>
|
||||
<li>testPawn_3</li>
|
||||
</textures>
|
||||
</idle_up>
|
||||
<idle_down>
|
||||
<textures>
|
||||
<li>testPawn_4</li>
|
||||
</animationTextures>
|
||||
</textures>
|
||||
</idle_down>
|
||||
<idle_left>
|
||||
<textures>
|
||||
<li>testPawn_8</li>
|
||||
</textures>
|
||||
</idle_left>
|
||||
<idle_right>
|
||||
<textures>
|
||||
<li>testPawn_12</li>
|
||||
</textures>
|
||||
</idle_right>
|
||||
|
||||
<!-- Walk states -->
|
||||
<walk_up>
|
||||
<textures>
|
||||
<li>testPawn_1</li>
|
||||
</textures>
|
||||
</walk_up>
|
||||
<walk_down>
|
||||
<textures>
|
||||
<li>testPawn_5</li>
|
||||
</textures>
|
||||
</walk_down>
|
||||
<walk_left>
|
||||
<textures>
|
||||
<li>testPawn_9</li>
|
||||
</textures>
|
||||
</walk_left>
|
||||
<walk_right>
|
||||
<textures>
|
||||
<li>testPawn_13</li>
|
||||
</textures>
|
||||
</walk_right>
|
||||
|
||||
<!-- Melee Attack states -->
|
||||
<meleeAttack_up>
|
||||
<textures>
|
||||
<li>testPawn_2</li>
|
||||
</textures>
|
||||
</meleeAttack_up>
|
||||
<meleeAttack_down>
|
||||
<textures>
|
||||
<li>testPawn_6</li>
|
||||
</textures>
|
||||
</meleeAttack_down>
|
||||
<meleeAttack_left>
|
||||
<textures>
|
||||
<li>testPawn_10</li>
|
||||
</textures>
|
||||
</meleeAttack_left>
|
||||
<meleeAttack_right>
|
||||
<textures>
|
||||
<li>testPawn_14</li>
|
||||
</textures>
|
||||
</meleeAttack_right>
|
||||
|
||||
<!-- Ranged Attack states -->
|
||||
<rangedAttack_up>
|
||||
<textures>
|
||||
<li>testPawn_3</li>
|
||||
</textures>
|
||||
</rangedAttack_up>
|
||||
<rangedAttack_down>
|
||||
<textures>
|
||||
<li>testPawn_7</li>
|
||||
</textures>
|
||||
</rangedAttack_down>
|
||||
<rangedAttack_left>
|
||||
<textures>
|
||||
<li>testPawn_11</li>
|
||||
</textures>
|
||||
</rangedAttack_left>
|
||||
<rangedAttack_right>
|
||||
<textures>
|
||||
<li>testPawn_15</li>
|
||||
</textures>
|
||||
</rangedAttack_right>
|
||||
</drawingOrder>
|
||||
<behaviorTree>
|
||||
<Node className="JobGiver_RandomWander"/>
|
||||
@ -44,10 +128,18 @@
|
||||
</attributes>
|
||||
<affiliation>player</affiliation>
|
||||
<drawingOrder>
|
||||
<texturePath>res:Character\Test</texturePath>
|
||||
<drawingOrder_down>
|
||||
<node name="Body"/>
|
||||
</drawingOrder_down>
|
||||
<idle_down name="Body" FPS="4">
|
||||
<textures>
|
||||
<li>testPawnAnimation_0</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_4</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
</textures>
|
||||
</idle_down>
|
||||
</drawingOrder>
|
||||
<behaviorTree>
|
||||
<Node className="JobGiver_RandomWander"/>
|
||||
@ -64,11 +156,18 @@
|
||||
</attributes>
|
||||
<affiliation>player</affiliation>
|
||||
<drawingOrder>
|
||||
<texturePath>res:Character\HighSpeed</texturePath>
|
||||
<pixelsPerUnit>200</pixelsPerUnit>
|
||||
<drawingOrder_down>
|
||||
<node name="Body"/>
|
||||
</drawingOrder_down>
|
||||
<idle_down name="Body" FPS="4">
|
||||
<textures>
|
||||
<li>testPawnAnimation_0</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_4</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
</textures>
|
||||
</idle_down>
|
||||
</drawingOrder>
|
||||
<behaviorTree>
|
||||
<Node className="JobGiver_Idel"/>
|
||||
@ -85,11 +184,18 @@
|
||||
</attributes>
|
||||
<affiliation>player</affiliation>
|
||||
<drawingOrder>
|
||||
<texturePath>res:Character\HighSpeed</texturePath>
|
||||
<pixelsPerUnit>200</pixelsPerUnit>
|
||||
<drawingOrder_down>
|
||||
<node name="Body" />
|
||||
</drawingOrder_down>
|
||||
<idle_down name="Body" FPS="4">
|
||||
<textures>
|
||||
<li>testPawnAnimation_0</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_4</li>
|
||||
<li>testPawnAnimation_3</li>
|
||||
<li>testPawnAnimation_2</li>
|
||||
<li>testPawnAnimation_1</li>
|
||||
</textures>
|
||||
</idle_down>
|
||||
</drawingOrder>
|
||||
<behaviorTree>
|
||||
<Node className="JobGiver_Idel"/>
|
||||
|
BIN
Client/Data/Core/Resources/Character/test.png
Normal file
BIN
Client/Data/Core/Resources/Character/test.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 734 B |
Reference in New Issue
Block a user