研究归属: Project Vampirefall - Tech/Mechanics
创建日期: 2025-12-04
优先级: ⭐⭐⭐⭐ (中高)
移动与冲刺系统是动作游戏中最基础的交互层。它不仅决定了玩家如何穿越空间,更是战斗节奏的核心调节器。在 Vampirefall 这种混合品类(塔防 + Roguelike)中,移动系统还承担着”在塔之间快速穿梭”的功能性需求。
关键概念:
冲刺不应是线性的位移,而应该具有爆发感。通常使用 对数衰减 或 自定义贝塞尔曲线。
Velocity(t) = DashSpeed * (1 - (t / DashDuration)^Power)
其中:
- DashSpeed: 初始爆发速度
- DashDuration: 冲刺总时长
- Power: 衰减指数 (通常 > 1,如 2 或 3,使速度快速下降)
timeline
title 冲刺过程时间轴
0ms : 冲刺开始 (Start)
0-50ms : 前摇 (Startup) - 有碰撞体积
50-250ms : 无敌帧 (I-Frames) - 免疫伤害
250-350ms : 后摇 (Recovery) - 速度衰减,无敌结束
350ms : 冲刺结束 (End)
移动本身应该是有趣的。
Vampirefall 的地图充满了塔基和怪物。
采用 充能点数 (Charges) 系统而非耐力条 (Stamina Bar)。
[System.Serializable]
public class MovementConfig
{
[Header("基础移动")]
public float moveSpeed = 8.0f;
public float acceleration = 50.0f; // 起步加速度
public float deceleration = 60.0f; // 停止摩擦力
[Header("冲刺参数")]
public float dashDistance = 6.0f;
public float dashDuration = 0.3f;
public AnimationCurve dashSpeedCurve; // 速度变化曲线
[Header("冷却与资源")]
public int maxDashCharges = 2;
public float dashCooldown = 1.2f; // 充能恢复时间
public float dashInternalCooldown = 0.15f; // 连续冲刺最小间隔
[Header("高级机制")]
public float iframeDuration = 0.2f; // 无敌时间
public bool canDashThroughEnemies = true;
public LayerMask obstacleLayer; // 阻挡层(墙壁、塔基)
}
public class PlayerMovement : MonoBehaviour
{
public MovementConfig config;
private CharacterController cc;
private Vector3 velocity;
private float currentDashTime;
private Vector3 dashDirection;
private enum State { Normal, Dashing, Stunned }
private State currentState;
void Update()
{
switch (currentState)
{
case State.Normal:
HandleNormalMovement();
HandleDashInput();
break;
case State.Dashing:
HandleDashMovement();
break;
}
}
void HandleNormalMovement()
{
Vector3 input = GetInputVector();
// 简单的加减速处理
if (input.magnitude > 0.1f)
{
// 加速向目标速度
Vector3 targetVel = input * config.moveSpeed;
velocity = Vector3.MoveTowards(velocity, targetVel, config.acceleration * Time.deltaTime);
}
else
{
// 减速至静止
velocity = Vector3.MoveTowards(velocity, Vector3.zero, config.deceleration * Time.deltaTime);
}
cc.Move(velocity * Time.deltaTime);
// 朝向移动方向
if (velocity.sqrMagnitude > 0.1f)
transform.forward = velocity.normalized;
}
void StartDash()
{
if (dashCharges <= 0) return;
dashCharges--;
currentState = State.Dashing;
currentDashTime = 0f;
// 确定冲刺方向:如果有输入则按输入方向,否则按角色朝向
Vector3 input = GetInputVector();
dashDirection = input.magnitude > 0.1f ? input.normalized : transform.forward;
// 开启无敌
StartCoroutine(IFrameRoutine());
// 播放特效和音效
PlayDashEffects();
}
void HandleDashMovement()
{
currentDashTime += Time.deltaTime;
float progress = currentDashTime / config.dashDuration;
if (progress >= 1f)
{
EndDash();
return;
}
// 采样速度曲线
float speedMultiplier = config.dashSpeedCurve.Evaluate(progress);
float currentSpeed = (config.dashDistance / config.dashDuration) * speedMultiplier;
cc.Move(dashDirection * currentSpeed * Time.deltaTime);
}
}
为了实现”穿过敌人但被墙挡住”,我们需要动态调整 Layer Collision Matrix 或 Collider 设置。
IEnumerator IFrameRoutine()
{
// 1. 开启无敌状态(免疫伤害逻辑在 Health 组件中检查此标志)
isInvincible = true;
// 2. 忽略与敌人的物理碰撞(防止被卡住)
Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer("Enemy"), true);
yield return new WaitForSeconds(config.iframeDuration);
// 3. 恢复碰撞
Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer("Enemy"), false);
isInvincible = false;
}
[!WARNING] 穿模风险: 如果冲刺结束时玩家正好重叠在敌人体内,物理引擎可能会产生巨大的排斥力将玩家弹飞。 解决方案:
- 保持软碰撞(Soft Collision),允许重叠但施加持续推力分离。
- 或者在冲刺结束检测重叠,如果重叠则向最近的空地微推一段距离。
// 简单的转角修正逻辑示意
void HandleCornerSliding(Vector3 moveDir)
{
if (Physics.Raycast(transform.position, moveDir, out RaycastHit hit, 1f, obstacleLayer))
{
// 如果撞墙角度很小(擦边),则修正移动方向沿墙面滑动
Vector3 slideDir = Vector3.ProjectOnPlane(moveDir, hit.normal).normalized;
cc.Move(slideDir * speed * Time.deltaTime);
}
}
文档版本: v1.0
最后更新: 2025-12-04
维护者: Vampirefall Tech Team