程序视角下的“连跳”操作(FPS游戏)

前言:
本文转载自http://adrianb.io/2015/02/14/bunnyhop.html
作者Adrian Biagioli(Flafla2)是CMU的游戏引擎开发大佬
这篇技术分享解释了FPS游戏中,身连跳身法,或者说B-hop的机制。它解释了我心中一直以来的疑问,同时浅显易懂,所以我意图在中文互联网上进行分享。

Bunnyhopping from the Programmer’s Perspective

Technical Writeup Posted on 14 February 2015 by Flafla2

在Quake III Arena, Half-Life, 以及 Counter-Strike 等游戏中,有这样一个著名的bug,即玩家在跳起在空中的时候,如果使用键盘A/D进行位移的同时,鼠标向左/右转动视角,角色移速将突破游戏设定的“最大速度”。玩家将利用这一漏洞实现快速移动的技术叫做bunny hopping(BHop)

链接:
Bhop的一个例子 Counter-Strike: Source (Source)

空中摆头(Air Strafing)

在这个技巧中,空中摆头(Air Strafing)是关键,按A时鼠标也要左移,同理按D和右移鼠标一起进行。两个动作同步练好了,Bhop才跳的漂亮。

Air Strafing的数学解释

这个机制起源于Quake 引擎中对加速度的处理方式。感兴趣可以查看其源码 Quake III movement code或者Half Life 2 movement code

在 Quake III引擎中,角色的速度实际上是通过限制当前速度在加速度方向上的投影完成的,而不是直接限制速度。

投影

在这里插入图片描述
Figure 1: a在b上的投影

限制投影的数学模型

在这里插入图片描述

Figure 2: 使用投影限制速度. “Time 0” is on the top left, Time 1 is on the top right, etc. Here is the key to this diagram:
图标含义解释:
Vc = The current velocity before any calculations
Vw = The direction that the player wants to move in (the so-called wish direction).
Vp = Vc projected onto Vw. Keep in mind that we are only considering magnitude in this calculation, so the direction of the projection doesn’t matter.
Va = The acceleration to be added to Vp. The magnitude of this acceleration is server-defined.
Vmax = The server-defined maximum velocity. If Vp + Va exceeds this, then Va is truncated.
上图是玩家按住键盘A+鼠标左移时经历的4个tick。在最后一个tick时Vp超过了服务器定义的最大限速,并被裁剪到Vmax,同时Va也被截断为0。但不管怎样,Vc都远超Vmax!

上代码

原作者实现的加速函数(C#)

private Vector3 Accelerate(Vector3 accelDir, Vector3 prevVelocity, float accelerate, float max_velocity)
{
    float projVel = Vector3.Dot(prevVelocity, accelDir); // Vector projection of Current velocity onto accelDir.
    float accelVel = accelerate * Time.fixedDeltaTime; // Accelerated velocity in direction of movment

    // If necessary, truncate the accelerated velocity so the vector projection does not exceed max_velocity
    if(projVel + accelVel > max_velocity)
        accelVel = max_velocity - projVel;

    return prevVelocity + accelDir * accelVel;
}

摩擦力

由于摩擦力的存在,玩家必须在空中完成摆头。否则,在地面上摆头时摩擦力将抵消角色的速度。但跳跃着地总有摩擦力生效的时候,为什么身法大师能够克服摩擦力,而菜鸟却总是被摩擦机制减速呢?

很简单,也很困难。在Quake 或者CS起源的引擎中,跳跃着地时有1帧的窗口是不施加摩擦力的。只要在这一帧有跳起的动作就好了。这也是连跳身法操作困难的原因。

摩擦计算非常简单,代码如下:

float speed = prevVelocity.magnitude;
if (speed != 0) // To avoid divide by zero errors
{
    float drop = speed * friction * Time.fixedDeltaTime;
    prevVelocity *= Mathf.Max(speed - drop, 0) / speed; // Scale the velocity based on friction.
}

当然,只有接触地面才会施加摩擦力。摩擦力是一个服务器定义的变量,范围大约为1-5。如果你熟悉源引擎中的控制台命令,则可以将此变量识别为sv_friction。

联合作用

以下是代码中的所有内容

// accelDir: normalized direction that the player has requested to move (taking into account the movement keys and look direction)
// prevVelocity: The current velocity of the player, before any additional calculations
// accelerate: The server-defined player acceleration value
// max_velocity: The server-defined maximum player velocity (this is not strictly adhered to due to strafejumping)
private Vector3 Accelerate(Vector3 accelDir, Vector3 prevVelocity, float accelerate, float max_velocity)
{
    float projVel = Vector3.Dot(prevVelocity, accelDir); // Vector projection of Current velocity onto accelDir.
    float accelVel = accelerate * Time.fixedDeltaTime; // Accelerated velocity in direction of movment

    // If necessary, truncate the accelerated velocity so the vector projection does not exceed max_velocity
    if(projVel + accelVel > max_velocity)
        accelVel = max_velocity - projVel;

    return prevVelocity + accelDir * accelVel;
}

private Vector3 MoveGround(Vector3 accelDir, Vector3 prevVelocity)
{
    // Apply Friction
    float speed = prevVelocity.magnitude;
    if (speed != 0) // To avoid divide by zero errors
    {
        float drop = speed * friction * Time.fixedDeltaTime;
        prevVelocity *= Mathf.Max(speed - drop, 0) / speed; // Scale the velocity based on friction.
    }

    // ground_accelerate and max_velocity_ground are server-defined movement variables
    return Accelerate(accelDir, prevVelocity, ground_accelerate, max_velocity_ground);
}

private Vector3 MoveAir(Vector3 accelDir, Vector3 prevVelocity)
{
    // air_accelerate and max_velocity_air are server-defined movement variables
    return Accelerate(accelDir, prevVelocity, air_accelerate, max_velocity_air);
}

熟悉Source引擎的人可能会再次认识到这段代码中的sv_accelerate、sv_airaccelerate和sv_frication转换。你可以花一些时间来调整这些服务器定义的变量,使其符合您的喜好,因为它们决定了游戏的运动效果。

That’s it! This should be all you need to implement bunnyhopping into your game. If you have any questions or comments please feel free to post in the comments section below. Thank you for reading!

References

Quake III movement code - This is the original Quake engine movment code that “started it all”. Check this out for nostalgia. There is a lot of engine-specific/messy code in there so watch out.
Source Engine movement code - This code was based off of the Quake engine code. However, it is a bit easier to decipher than the Quake engine. It also has some old code from an unreleased TF2 “Commander Mode” which is pretty cool.
“How do I implement strafe-jumping?” - This is a gamedev stackexchange question that is pretty enlightening on the maths behind strafejumping.
F3Quake - Strafing Theory - This is a player-created mathematical analysis on strafe jumping which seeks to find the mathematically optimal air strafing strategy. It’s an interesting read for any math nerd. It also details the worth of implementing bunnyhopping for the sake of a hardcore community.


译者瞎言:
我也曾是FPS玩家,技术糟糕的我对游戏中身法连跳的玩家感到十分恼怒(什么破游戏机制,一点也不真实!Bug为什么不修,一群牛鬼蛇神跳来跳去还打什么?)。
但看完这篇解释,我也觉得在意料之中(可接受范围内)。就像开车一样,油门控制的本质上是加速度而不是速度。考驾照时我常常幻想是在操控一款俯视的开车游戏:要是方向盘一打车身就能变向就好了,但往往要经过一段距离才可以做到前轮和车身平行。这种困难来自于人类对加速度的控制力、感知力比不上速度本身。
实际上直接操纵角色速度的游戏虽然很爽(无延迟),但缺乏了真实感。我的印象中FLASH游戏闪客快打3就可以在奔跑中立即变方向。
我不是搞3D游戏引擎的,也无意进行交互设计。但这篇文章从原理上解释了世界,同时也让我看到了数学、物理在程序设计中“注入灵魂”式的作用。在今天,乃至雷神之锤那个时代,能体会到“编程的数学之美”的,可能终究是少数吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值