188 lines
6.3 KiB
C#
188 lines
6.3 KiB
C#
using Godot;
|
|
|
|
namespace Milimoe.GodotGame
|
|
{
|
|
public partial class CharacterBody : CharacterBody2D
|
|
{
|
|
/// <summary>
|
|
/// 移动速度 (像素/秒)
|
|
/// </summary>
|
|
[Export]
|
|
public float Speed = 100f;
|
|
|
|
/// <summary>
|
|
/// 加速度 (像素/秒^2) - 控制从静止加速到最大速度的快慢
|
|
/// 值越大,加速越快,惯性越小。
|
|
/// </summary>
|
|
[Export]
|
|
public float Acceleration = 500f;
|
|
|
|
/// <summary>
|
|
/// 减速度 (像素/秒^2) - 控制从运动减速到静止的快慢
|
|
/// 值越大,减速越快,惯性越小。
|
|
/// 通常减速度可以比加速度大,让停止更迅速。
|
|
/// </summary>
|
|
[Export]
|
|
public float Deceleration = 800f;
|
|
|
|
/// <summary>
|
|
/// 静止图片
|
|
/// </summary>
|
|
[Export]
|
|
public Texture2D IdleTexture;
|
|
|
|
/// <summary>
|
|
/// 行走图片
|
|
/// </summary>
|
|
[Export]
|
|
public Texture2D WalkTexture;
|
|
|
|
/// <summary>
|
|
/// 精灵节点
|
|
/// </summary>
|
|
[Export]
|
|
public AnimatedSprite2D AnimatedSprite2D;
|
|
|
|
/// <summary>
|
|
/// 战斗开始信号
|
|
/// </summary>
|
|
/// <param name="player"></param>
|
|
/// <param name="enemy"></param>
|
|
[Signal]
|
|
public delegate void BattleStartedEventHandler(CharacterBody2D player, CharacterBody2D enemy);
|
|
|
|
/// <summary>
|
|
/// _PhysicsProcess 是 Godot 进行物理计算和运动更新的函数
|
|
/// </summary>
|
|
/// <param name="delta"></param>
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
if (GameConstant.Pause)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 获取输入向量
|
|
// 获取水平输入轴的值 (-1 到 1)
|
|
float inputX = Input.GetAxis("move_left", "move_right");
|
|
// 获取垂直输入轴的值 (-1 到 1)
|
|
float inputY = Input.GetAxis("move_up", "move_down");
|
|
|
|
// 创建一个表示输入方向的向量
|
|
Vector2 inputVector = new(inputX, inputY);
|
|
|
|
// 目标速度
|
|
Vector2 targetVelocity;
|
|
// 当前帧应该使用的加速度/减速度
|
|
float currentAcceleration;
|
|
|
|
if (inputVector.LengthSquared() > 0)
|
|
{
|
|
// 有输入时,目标速度是输入方向乘以最大速度
|
|
targetVelocity = inputVector.Normalized() * Speed;
|
|
// 使用加速度
|
|
currentAcceleration = Acceleration;
|
|
}
|
|
else
|
|
{
|
|
// 没有输入时,目标速度是零
|
|
targetVelocity = Vector2.Zero;
|
|
// 使用减速度
|
|
currentAcceleration = Deceleration;
|
|
}
|
|
|
|
// 使用 Mathf.MoveToward 平滑地改变当前速度到目标速度
|
|
Velocity = Velocity.MoveToward(targetVelocity, currentAcceleration * (float)delta);
|
|
|
|
// 处理与墙壁、障碍物等的碰撞,阻止角色穿过它们
|
|
MoveAndSlide();
|
|
|
|
UpdateAnimation();
|
|
|
|
for (int i = 0; i < GetSlideCollisionCount(); i++)
|
|
{
|
|
KinematicCollision2D collision = GetSlideCollision(i);
|
|
GodotObject collider = collision.GetCollider();
|
|
if (collider is EnemyBody enemy && enemy.Enabled && !enemy.Dead && enemy.CollisionLayer == 4)
|
|
{
|
|
GameConstant.Pause = true;
|
|
EmitSignal(SignalName.BattleStarted, this, enemy);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据速度更新贴图
|
|
/// </summary>
|
|
private void UpdateAnimation()
|
|
{
|
|
if (AnimatedSprite2D == null) return;
|
|
|
|
// 获取当前实时速度的长度
|
|
float currentSpeed = Velocity.Length();
|
|
|
|
if (currentSpeed > 0.1f) // 如果正在移动
|
|
{
|
|
// 播放行走动画
|
|
if (AnimatedSprite2D.Animation != "walk")
|
|
{
|
|
AnimatedSprite2D.Play("walk");
|
|
}
|
|
|
|
// --- 核心算法:动画速度匹配 ---
|
|
// 计算比例:当前速度越快,动画播得越快
|
|
// 1.2f 是一个“手感系数”,如果觉得脚还是打滑,就调大这个值
|
|
float speedFactor = currentSpeed / Speed;
|
|
AnimatedSprite2D.SpeedScale = speedFactor * 5f;
|
|
|
|
// 处理左右翻转
|
|
if (Velocity.X != 0)
|
|
{
|
|
AnimatedSprite2D.FlipH = Velocity.X < 0;
|
|
}
|
|
}
|
|
else // 停止移动
|
|
{
|
|
AnimatedSprite2D.Play("idle");
|
|
AnimatedSprite2D.SpeedScale = 1.0f; // 回归正常倍率
|
|
}
|
|
}
|
|
|
|
// 用于存储返回时的位置
|
|
private Vector2 _savedOverworldPosition;
|
|
|
|
/// <summary>
|
|
/// 记录当前位置并传送到指定的 Tile 坐标
|
|
/// </summary>
|
|
/// <param name="targetLayer">目标 TileMapLayer</param>
|
|
/// <param name="tileCoords">目标格子坐标 (例如 new Vector2I(88, 4))</param>
|
|
public void TeleportToTile(TileMapLayer targetLayer, Vector2I tileCoords)
|
|
{
|
|
// 1. 记录当前的绝对坐标 (用于战斗结束后传回来)
|
|
_savedOverworldPosition = this.GlobalPosition;
|
|
GD.Print($"已记录当前位置: {_savedOverworldPosition}");
|
|
|
|
// 2. 将 Tile 坐标转换为该图层的本地像素坐标
|
|
// MapToLocal 返回的是该格子中心的相对坐标
|
|
Vector2 localPos = targetLayer.MapToLocal(tileCoords);
|
|
|
|
// 3. 将本地坐标转换为全局绝对坐标
|
|
Vector2 globalPos = targetLayer.ToGlobal(localPos);
|
|
|
|
// 4. 设置角色的全局位置(实现瞬移)
|
|
this.GlobalPosition = globalPos;
|
|
|
|
GD.Print($"角色已传送到 Tile {tileCoords},绝对坐标为: {globalPos}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 返回之前记录的位置
|
|
/// </summary>
|
|
public void ReturnToSavedPosition()
|
|
{
|
|
this.GlobalPosition = _savedOverworldPosition;
|
|
}
|
|
}
|
|
}
|