初次提交

This commit is contained in:
m0_75251201
2025-07-12 11:30:22 +08:00
commit 7700703099
1156 changed files with 21532 additions and 0 deletions

View File

@ -0,0 +1,71 @@
using Godot;
using System;
using System.Collections.Generic;
namespace Cosmobox
{
public partial class BagItemList
{
public List<BagItem> Items = new();
// 查找物品
public BagItem Find(string name)
{
foreach (var item in Items)
{
if (item.ItemName == name)
{
return item;
}
}
return null;
}
// 添加物品
public bool AddItem(ItemResource resource, int amount = 1)
{
if (resource == null || amount <= 0)
{
GD.PrintErr("Invalid item or amount.");
return false;
}
// 检查是否已有相同物品
var existingItem = Find(resource.ItemName);
if (existingItem != null && resource.Stackable)
{
existingItem.AddAmount(amount); // 堆叠物品
}
else
{
// 创建新物品条目
var newItem = new BagItem
{
itemResource = resource,
amount = amount
};
Items.Add(newItem);
}
return true;
}
// 移除物品
public bool RemoveItem(string name, int amount = 1)
{
var item = Find(name);
if (item == null)
{
GD.Print($"Item '{name}' not found in bag.");
return false;
}
if (amount >= item.amount)
{
Items.Remove(item); // 完全移除物品
return true;
}
item.RemoveAmount(amount); // 减少数量
return true;
}
}
}

View File

@ -0,0 +1 @@
uid://b1fsx76po5ksu

View File

@ -0,0 +1,225 @@
using Godot;
using System;
namespace Cosmobox
{
public partial class CameraControl : Camera2D
{
[Export] public Node2D Target; // 要跟随的目标(通常为玩家角色)
[Export] public bool EnableSmoothing = true; // 启用平滑跟随
[Export(PropertyHint.Range, "0.1,10,0.1")]
public float SmoothingSpeed = 2.5f; // 平滑跟随速度推荐值2.0-4.0
[Export] public bool EnableBounds = false; // 启用边界限制
[Export] public Rect2 CameraBounds; // 摄像机移动边界
[Export] public Vector2 PositionOffset = Vector2.Zero; // 目标位置偏移
[Export] public bool ScaleWithZoom = true; // 边界是否随缩放调整
[Export] public float DeadZoneRadius = 0; // 摄像机不移动的半径(减少微小移动)
[Export] public float ZoomSensitivity = 0.1f; // 缩放灵敏度
[Export] public Vector2 MinZoom = new Vector2(0.5f, 0.5f); // 最小缩放
[Export] public Vector2 MaxZoom = new Vector2(2.0f, 2.0f); // 最大缩放
private Vector2 _targetPosition;
private Vector2 _currentVelocity = Vector2.Zero; // 用于平滑插值
private Rect2 _effectiveBounds; // 实际使用的边界(考虑缩放)
public override void _Ready()
{
if (Target == null)
{
// 自动查找玩家作为目标
Target = GetTree().Root.GetNodeOrNull<Node2D>("Player");
if (Target == null)
{
GD.PrintErr("CameraControl: Failed to find Player node. Assign a Target manually.");
}
}
// 确保摄像机位置初始化
if (Target != null)
{
_targetPosition = Target.GlobalPosition + PositionOffset;
GlobalPosition = _targetPosition;
}
// 初始化有效边界
UpdateEffectiveBounds();
}
public override void _PhysicsProcess(double delta)
{
if (Target == null)
return;
float deltaFloat = (float)delta;
// 更新目标位置(考虑偏移)
Vector2 newTarget = Target.GlobalPosition + PositionOffset;
// 应用死区减少微小移动
if (DeadZoneRadius > 0 && newTarget.DistanceTo(_targetPosition) < DeadZoneRadius)
{
newTarget = _targetPosition;
}
else
{
_targetPosition = newTarget;
}
// 应用边界限制
if (EnableBounds)
{
UpdateEffectiveBounds();
_targetPosition = ClampPosition(_targetPosition);
}
// 应用平滑过渡或直接跟随
Vector2 newPosition;
if (EnableSmoothing && SmoothingSpeed > 0)
{
// 使用SmoothDamp获得更自然的缓动效果
newPosition = SmoothDamp(GlobalPosition, _targetPosition, ref _currentVelocity, 1 / SmoothingSpeed, deltaFloat);
}
else
{
newPosition = _targetPosition;
}
// 最终位置赋值
GlobalPosition = newPosition;
}
public override void _UnhandledInput(InputEvent @event)
{
// 鼠标滚轮缩放控制
if (@event is InputEventMouseButton mouseEvent)
{
if (mouseEvent.IsPressed())
{
Vector2 zoomFactor = Zoom;
// // 滚轮向上 - 放大
// if (mouseEvent.ButtonIndex == MouseButton.WheelUp)
// {
// zoomFactor -= Vector2.One * ZoomSensitivity;
// }
// // 滚轮向下 - 缩小
// else if (mouseEvent.ButtonIndex == MouseButton.WheelDown)
// {
// zoomFactor += Vector2.One * ZoomSensitivity;
// }
// // 钳制缩放值
// zoomFactor.X = Mathf.Clamp(zoomFactor.X, MinZoom.X, MaxZoom.X);
// zoomFactor.Y = Mathf.Clamp(zoomFactor.Y, MinZoom.Y, MaxZoom.Y);
if (zoomFactor != Zoom)
{
Zoom = zoomFactor;
UpdateEffectiveBounds();
}
}
}
}
/// <summary>
/// 平滑插值函数类似Unity的SmoothDamp
/// </summary>
private Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime, float deltaTime, float maxSpeed = float.MaxValue)
{
float omega = 2f / Mathf.Max(0.0001f, smoothTime);
float x = omega * deltaTime;
float exp = 1f / (1f + x + 0.48f * x * x + 0.235f * x * x * x);
Vector2 change = current - target;
Vector2 originalTarget = target;
float maxChange = maxSpeed * smoothTime;
// 使用自定义方法替代ClampMagnitude
float magnitude = change.Length();
if (magnitude > maxChange && maxChange > 0)
{
change = change.Normalized() * maxChange;
}
target = current - change;
Vector2 temp = (currentVelocity + omega * change) * deltaTime;
currentVelocity = (currentVelocity - omega * temp) * exp;
Vector2 result = target + (change + temp) * exp;
// 防止过冲
Vector2 toOriginalTarget = originalTarget - current;
Vector2 toResult = result - originalTarget;
if (toOriginalTarget.Dot(toResult) > 0)
{
result = originalTarget;
currentVelocity = (result - originalTarget) / deltaTime;
}
return result;
}
/// <summary>
/// 更新实际使用的边界(考虑缩放)
/// </summary>
private void UpdateEffectiveBounds()
{
if (!EnableBounds || !ScaleWithZoom)
{
_effectiveBounds = CameraBounds;
return;
}
// 根据缩放比例调整边界
Vector2 halfViewSize = GetViewportRect().Size * 0.5f / Zoom;
Vector2 min = CameraBounds.Position + halfViewSize;
Vector2 max = CameraBounds.End - halfViewSize;
// 确保有效边界有效
if (min.X > max.X) min.X = max.X = (min.X + max.X) * 0.5f;
if (min.Y > max.Y) min.Y = max.Y = (min.Y + max.Y) * 0.5f;
_effectiveBounds = new Rect2(min, max - min);
}
/// <summary>
/// 钳制位置在边界范围内
/// </summary>
private Vector2 ClampPosition(Vector2 position)
{
return new Vector2(
Mathf.Clamp(position.X, _effectiveBounds.Position.X, _effectiveBounds.End.X),
Mathf.Clamp(position.Y, _effectiveBounds.Position.Y, _effectiveBounds.End.Y)
);
}
// 调试:在编辑器中绘制边界可视化
public override void _Draw()
{
if (Engine.IsEditorHint() && EnableBounds)
{
// 绘制主边界
var rect = new Rect2(_effectiveBounds.Position - GlobalPosition, _effectiveBounds.Size);
DrawRect(rect, Colors.Red, false, 2.0f);
// 绘制原始边界参考线
var origRect = new Rect2(CameraBounds.Position - GlobalPosition, CameraBounds.Size);
DrawRect(origRect, Colors.Yellow, false, 1.0f);
}
}
/// <summary>
/// 设置摄像机边界简化API
/// </summary>
public void SetBounds(Rect2 bounds, bool updateImmediately = true)
{
CameraBounds = bounds;
EnableBounds = true;
if (updateImmediately)
UpdateEffectiveBounds();
}
}
}

View File

@ -0,0 +1 @@
uid://bhpimjcr0h66h

View File

@ -0,0 +1,232 @@
using Godot;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
namespace Cosmobox
{
public static class CharacterLoader
{
// 支持的部件目录列表
private static readonly string[] ComponentFolders = {
"body", "clothing", "hair", "hairBackground", "head", "leftEar", "rightEar"
};
// 方向关键词映射到索引位置
private static readonly Dictionary<string, int> DirectionMapping = new Dictionary<string, int>(System.StringComparer.OrdinalIgnoreCase)
{
{"south", 0}, // 南方向 - 索引0
{"north", 1}, // 北方向 - 索引1
{"east", 2}, // 东方向 - 索引2
{"west", 2} // 西方向也映射到索引2共用东方向的纹理
};
/// <summary>
/// 加载角色所有部件的纹理
/// </summary>
public static Dictionary<string, Texture2D[]> LoadCharacterTextures(string rootPath)
{
var textures = new Dictionary<string, Texture2D[]>();
// GD.Print($"开始加载角色纹理资源,根目录: {rootPath}");
// 检查根目录是否存在
if (!DirAccess.DirExistsAbsolute(rootPath))
{
// GD.PrintErr($"错误:资源根目录不存在 - {rootPath}");
return textures;
}
using (var dir = DirAccess.Open(rootPath))
{
if (dir == null)
{
GD.PrintErr($"错误:无法打开根目录 - {rootPath}");
return textures;
}
// GD.Print($"找到根目录,开始扫描部件文件夹...");
// 遍历所有支持的部件目录
foreach (string component in ComponentFolders)
{
string componentPath = Path.Combine(rootPath, component);
// GD.Print($"检查部件: {component},路径: {componentPath}");
if (DirAccess.DirExistsAbsolute(componentPath))
{
// GD.Print($"找到部件文件夹: {component}");
Texture2D[] componentTextures = LoadComponentTextures(componentPath);
if (componentTextures != null && componentTextures.Length == 3)
{
// GD.Print($"成功加载部件: {component},纹理数量: 3");
textures[component] = componentTextures;
}
else
{
GD.PrintErr($"警告:部件{component}纹理加载不完整,实际数量: {componentTextures?.Length ?? 0}");
}
}
else
{
// GD.Print($"部件文件夹不存在: {component}");
}
}
}
// GD.Print($"资源加载完成,成功部件: {textures.Count}/{ComponentFolders.Length}");
return textures;
}
/// <summary>
/// 加载单个部件的所有方向纹理
/// </summary>
private static Texture2D[] LoadComponentTextures(string folderPath)
{
Texture2D[] textures = new Texture2D[3];
var foundFiles = new List<(string path, int index)>();
// GD.Print($"加载部件文件夹: {folderPath}");
using (var dir = DirAccess.Open(folderPath))
{
if (dir == null)
{
GD.PrintErr($"错误:无法打开部件目录 - {folderPath}");
return textures;
}
var status = dir.ListDirBegin();
if (status != Error.Ok)
{
GD.PrintErr($"错误:无法开始文件列表 ({status}) - {folderPath}");
return textures;
}
string fileName = dir.GetNext();
int fileCount = 0;
int matchedCount = 0;
List<string> unmatchedFiles = new List<string>();
// GD.Print($"开始扫描文件...");
while (!string.IsNullOrEmpty(fileName))
{
fileCount++;
if (!dir.CurrentIsDir())
{
string filePath = Path.Combine(folderPath, fileName);
// GD.Print($"处理文件 #{fileCount}: {fileName}");
// 检查文件扩展名
if (!IsImageFile(fileName))
{
// GD.Print($"跳过非图片文件: {fileName}");
fileName = dir.GetNext();
continue;
}
bool matched = false;
// 检测方向关键词
foreach (var kv in DirectionMapping)
{
if (fileName.Contains(kv.Key, System.StringComparison.OrdinalIgnoreCase))
{
// GD.Print($"找到方向关键词 '{kv.Key}' -> 索引 {kv.Value}");
foundFiles.Add((filePath, kv.Value));
matched = true;
matchedCount++;
break;
}
}
if (!matched)
{
GD.PrintErr($"警告:无法识别的方向名称 - {fileName}");
unmatchedFiles.Add(fileName);
}
}
else
{
// GD.Print($"跳过子目录: {fileName}");
}
fileName = dir.GetNext();
}
dir.ListDirEnd();
// GD.Print($"文件夹扫描完成:");
// GD.Print($"- 总文件: {fileCount}");
// GD.Print($"- 匹配文件: {matchedCount}");
if (unmatchedFiles.Count > 0)
{
GD.PrintErr($"警告: {unmatchedFiles.Count} 个文件未包含方向关键词(south/north/east/west):");
foreach (var f in unmatchedFiles)
{
GD.PrintErr($" - {f}");
}
}
}
// 检查是否所有方向都找到了文件
bool[] foundDirections = new bool[3];
// 加载纹理
foreach (var (path, index) in foundFiles)
{
if (index >= 0 && index < textures.Length)
{
if (textures[index] == null)
{
// GD.Print($"加载纹理: {Path.GetFileName(path)} -> 方向索引 {index}");
textures[index] = ResourceLoader.Load<Texture2D>(path);
foundDirections[index] = true;
if (textures[index] == null)
{
GD.PrintErr($"错误:无法加载纹理资源 - {path}");
}
}
else
{
GD.Print($"跳过纹理: {Path.GetFileName(path)} -> 方向索引 {index}(已有更优先的纹理)");
}
}
else
{
GD.PrintErr($"错误:无效方向索引 {index} - {path}");
}
}
// 检查缺失的方向
for (int i = 0; i < foundDirections.Length; i++)
{
if (!foundDirections[i])
{
string direction;
if (i == 0) direction = "south";
else if (i == 1) direction = "north";
else direction = "east";
GD.PrintErr($"警告:缺少{direction}方向纹理");
}
}
return textures;
}
/// <summary>
/// 检查文件是否是图片格式
/// </summary>
private static bool IsImageFile(string fileName)
{
string ext = Path.GetExtension(fileName).ToLower();
return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" || ext == ".bmp";
}
}
}

View File

@ -0,0 +1 @@
uid://cvdj4q7yvd85a

252
Script/Pawn/Pawn.cs Normal file
View File

@ -0,0 +1,252 @@
using Godot;
using System;
using System.Collections.Generic;
namespace Cosmobox
{
public partial class Pawn : Sprite2D, IThingPhysics
{
// 移动参数
[Export] protected float moveSpeed = 200f; // 每秒移动的世界单位数
// 角色属性
[Export] protected int attack = 10; // 攻击力
[Export] protected int health = 100; // 生命值
[Export] protected int defense = 5; // 防御力
// 部件节点
[Export] protected Sprite2D head; // 头部精灵
[Export] protected Sprite2D body; // 身体精灵
[Export] protected Sprite2D clothes; // 衣服精灵
[Export] protected Sprite2D hairBackground; // 头发背景精灵
[Export] protected Sprite2D hair; // 头发精灵
[Export] protected Sprite2D leftEar; // 左耳精灵
[Export] protected Sprite2D rightEar; // 右耳精灵
// 资源路径
[Export] protected string characterResourcesPath = null; // 角色资源文件夹路径
// 纹理数组每个数组有3个方向下/上/侧边)
[Export] protected Texture2D[] headTextures = new Texture2D[3]; // 头部纹理
[Export] protected Texture2D[] bodyTextures = new Texture2D[3]; // 身体纹理
[Export] protected Texture2D[] clothesTextures = new Texture2D[3]; // 衣服纹理
[Export] protected Texture2D[] hairBackgroundTextures = new Texture2D[3]; // 头发背景纹理
[Export] protected Texture2D[] hairTextures = new Texture2D[3]; // 头发纹理
[Export] protected Texture2D[] leftEarTextures = new Texture2D[3]; // 左耳纹理
[Export] protected Texture2D[] rightEarTextures = new Texture2D[3]; // 右耳纹理
// 物理相关
protected Vector2 currentMovementInput = Vector2.Zero; // 当前原始的输入方向
public bool Moving
{
get
{
return currentMovementInput != Vector2.Zero;
}
}
public override void _Ready()
{
if (!string.IsNullOrEmpty(characterResourcesPath))
{
// 加载角色纹理
var loadedTextures = CharacterLoader.LoadCharacterTextures(characterResourcesPath);
// 应用加载的纹理
ApplyLoadedTextures(loadedTextures);
}
SetDownDirection(); // 初始化为向下方向
}
void IThingPhysics.PhysicsUpdate(double delta)
{
float deltaF = (float)delta;
// 根据输入方向和移动速度直接应用移动
// 使用Normalized()确保斜向移动不会更快
Position += currentMovementInput.Normalized() * moveSpeed * deltaF;
}
/// <summary>
/// 方向纹理设置方法
/// 每个方法设置角色在特定方向的纹理
/// </summary>
public void SetDownDirection() => SetTextures(0, false); // 设置向下方向的纹理
public void SetUpDirection() => SetTextures(1, false); // 设置向上方向的纹理
public void SetRightDirection() => SetTextures(2, false); // 设置向右方向的纹理
public void SetLeftDirection() => SetTextures(2, true); // 设置向左方向的纹理(使用翻转)
/// <summary>
/// 核心纹理设置方法
/// 为角色各部分设置纹理和水平翻转状态
/// </summary>
/// <param name="index">纹理索引0=下, 1=上, 2=侧边</param>
/// <param name="flipH">是否水平翻转(用于左右方向)</param>
private void SetTextures(int index, bool flipH)
{
// 为每个部件设置纹理
if (head != null) head.Texture = headTextures[index];
if (body != null) body.Texture = bodyTextures[index];
if (clothes != null) clothes.Texture = clothesTextures[index];
if (hairBackground != null) hairBackground.Texture = hairBackgroundTextures[index];
if (hair != null) hair.Texture = hairTextures[index];
// 武器纹理设置(需要时取消注释)
// if (weapon != null) weapon.Texture = weaponTextures[index];
// 设置每个部件的水平翻转状态
if (head != null) head.FlipH = flipH;
if (body != null) body.FlipH = flipH;
if (clothes != null) clothes.FlipH = flipH;
if (hairBackground != null) hairBackground.FlipH = flipH;
if (hair != null) hair.FlipH = flipH;
// 耳朵的特殊处理逻辑
if (leftEar != null && rightEar != null)
{
// 先设置耳朵纹理
leftEar.Texture = leftEarTextures[index];
rightEar.Texture = rightEarTextures[index];
// 当角色面向侧面时(左右方向)
if (index == 2)
{
if (flipH) // 面向左时
{
leftEar.Show(); // 显示左耳
rightEar.Hide(); // 隐藏右耳
}
else // 面向右时
{
leftEar.Hide(); // 隐藏左耳
rightEar.Show(); // 显示右耳
}
}
else // 上下方向,两个耳朵都显示
{
leftEar.Show();
rightEar.Show();
}
}
}
/// <summary>
/// 简化版移动处理 - 直接存储输入方向
/// 实际移动在物理处理过程中完成
/// </summary>
/// <param name="inputDirection">输入方向向量</param>
protected void HandleMovement(Vector2 inputDirection)
{
// 存储当前输入方向,移动计算在物理过程中处理
currentMovementInput = inputDirection;
}
/// <summary>
/// 根据移动向量更新角色朝向
/// </summary>
/// <param name="direction">移动方向向量</param>
public void UpdateDirection(Vector2 direction)
{
if (direction == Vector2.Zero) return; // 没有移动时不改变方向
// 计算角度0-360度
float angle = Mathf.RadToDeg(Mathf.Atan2(direction.Y, direction.X));
if (angle < 0) angle += 360;
// 根据角度范围决定方向
if (angle >= 45 && angle < 135)
{
SetDownDirection(); // 下方(例如输入 (0, 1)
}
else if (angle >= 135 && angle < 225)
{
SetLeftDirection(); // 左方(例如输入 (-1, 0)
}
else if (angle >= 225 && angle < 315)
{
SetUpDirection(); // 上方(例如输入 (0, -1)
}
else
{
SetRightDirection(); // 右方(例如输入 (1, 0)
}
}
/// <summary>
/// 应用加载的纹理到各个角色部件
/// </summary>
/// <param name="loadedTextures">加载的纹理字典</param>
private void ApplyLoadedTextures(Dictionary<string, Texture2D[]> loadedTextures)
{
// 头部纹理
if (loadedTextures.TryGetValue("head", out var headTex) && headTex.Length == 3)
{
headTextures = headTex;
}
// 身体纹理
if (loadedTextures.TryGetValue("body", out var bodyTex) && bodyTex.Length == 3)
{
bodyTextures = bodyTex;
}
// 衣服纹理
if (loadedTextures.TryGetValue("clothing", out var clothesTex) && clothesTex.Length == 3)
{
clothesTextures = clothesTex;
}
// 头发纹理
if (loadedTextures.TryGetValue("hair", out var hairTex) && hairTex.Length == 3)
{
hairTextures = hairTex;
}
// 头发背景纹理
if (loadedTextures.TryGetValue("hairBackground", out var hairBGTextures) && hairBGTextures.Length == 3)
{
hairBackgroundTextures = hairBGTextures;
}
// 耳朵纹理 - 分别处理左右耳
if (loadedTextures.TryGetValue("leftEar", out var leftEarTex) && leftEarTex.Length == 3)
{
leftEarTextures = leftEarTex;
}
if (loadedTextures.TryGetValue("rightEar", out var rightEarTex) && rightEarTex.Length == 3)
{
rightEarTextures = rightEarTex;
}
}
/// <summary>
/// 工具方法
/// </summary>
// 瞬间传送角色到指定位置
public void Teleport(Vector2 position) => Position = position;
// 设置移动速度确保不小于0
public void SetMoveSpeed(float speed) => moveSpeed = Mathf.Max(speed, 0);
// 获取当前速度矢量(方向向量 × 速度)
public Vector2 GetVelocity()
{
return currentMovementInput.Normalized() * moveSpeed;
}
/// <summary>
/// 添加瞬间冲量(直接改变位置)
/// 注意:对于持续的推动效果可能需要单独实现
/// </summary>
public void AddImpulse(Vector2 impulse)
{
// 直接修改位置 - 适用于瞬间位移
// 如需持续推动效果,需单独实现冲量系统
Position += impulse;
}
}
}

1
Script/Pawn/Pawn.cs.uid Normal file
View File

@ -0,0 +1 @@
uid://doim6711upf0a

141
Script/Pawn/Player.cs Normal file
View File

@ -0,0 +1,141 @@
using Godot;
using System;
using System.Collections.Generic; // 使用 List 和 Dictionary 需要这个命名空间
namespace Cosmobox
{
public partial class Player : Pawn, IThing
{
// === 玩家属性 ===
[Export] public Camera2D PlayerCamera; // 玩家摄像机
[Export] public Sprite2D AimCursor; // 瞄准光标精灵
[Export] public Vector2 AimOffset = Vector2.Zero; // 瞄准点偏移量
public BagItemList bagItem = new();
public override void _Ready()
{
// 调用基类的_Ready方法
base._Ready();
// 尝试查找摄像机(如果未分配)
if (PlayerCamera == null)
{
PlayerCamera = GetViewport().GetCamera2D();
if (PlayerCamera == null)
{
GD.PrintErr("Player: 未分配摄像机且视口中没有活动摄像机!");
}
}
// 初始化瞄准光标
if (AimCursor != null)
{
AimCursor.Visible = true;
}
}
public void Update(double delta)
{
// 获取输入方向
Vector2 inputDirection = GetInputDirection();
// 处理移动调用Pawn基类方法
HandleMovement(inputDirection);
// 更新角色朝向(鼠标方向)
UpdateDirectionToMouse();
}
/// <summary>
/// 更新角色朝向鼠标方向
/// </summary>
private void UpdateDirectionToMouse()
{
if (PlayerCamera == null) return;
// 获取鼠标在游戏世界中的位置
Vector2 mousePosition = GetGlobalMousePosition();
// 计算从玩家位置到鼠标位置的方向向量
Vector2 directionToMouse = (mousePosition - GlobalPosition).Normalized();
// 调用基类方法更新视觉方向
UpdateDirection(directionToMouse);
}
/// <summary>
/// 更新瞄准光标位置
/// </summary>
private void UpdateAimCursor()
{
if (AimCursor == null || PlayerCamera == null) return;
// 获取鼠标位置
Vector2 mousePosition = GetGlobalMousePosition();
// 设置光标位置(带偏移)
AimCursor.GlobalPosition = mousePosition + AimOffset;
}
/// <summary>
/// 获取键盘输入方向
/// </summary>
/// <returns>归一化的移动方向向量</returns>
private Vector2 GetInputDirection()
{
Vector2 direction = Vector2.Zero;
// 处理上下左右输入(确保项目设置中已配置这些输入)
if (Input.IsActionPressed("ui_up"))
{
direction.Y -= 1; // 上移
}
if (Input.IsActionPressed("ui_down"))
{
direction.Y += 1; // 下移
}
if (Input.IsActionPressed("ui_left"))
{
direction.X -= 1; // 左移
}
if (Input.IsActionPressed("ui_right"))
{
direction.X += 1; // 右移
}
return direction.Normalized(); // 确保斜向移动速度一致
}
/// <summary>
/// 获取瞄准方向(从玩家指向鼠标位置)
/// </summary>
public Vector2 GetAimDirection()
{
if (PlayerCamera == null) return Vector2.Zero;
Vector2 mousePosition = GetGlobalMousePosition();
return (mousePosition - GlobalPosition).Normalized();
}
/// <summary>
/// 获取瞄准角度(弧度)
/// </summary>
public float GetAimAngle()
{
Vector2 aimDir = GetAimDirection();
return Mathf.Atan2(aimDir.Y, aimDir.X);
}
/// <summary>
/// 获取瞄准角度(度)
/// </summary>
public float GetAimAngleDegrees()
{
return Mathf.RadToDeg(GetAimAngle());
}
}
}

View File

@ -0,0 +1 @@
uid://c1g503q7yoy1s