第1部分¶
教程介绍¶
本教程系列将向您展示如何制作一个单玩家的fps游戏。
在本教程系列的整个课程中,我们将介绍如何:
创造一个可以移动、冲刺和跳跃的第一人称角色。
制作用于处理动画转换的简单动画状态机。
要向第一人称角色添加三种武器,每种武器都使用不同的方式处理子弹碰撞:
一把刀(用 Area )
手枪(子弹场景)
步枪(使用 Raycast )
要在第一人称字符中添加两种不同类型的手榴弹:
普通的手榴弹
粘手榴弹
增加抓和扔的能力 RigidBody 结点
为播放器添加控制面板输入
为所有消耗弹药的武器增加弹药和重新装填。
增加弹药和健康装备
有两种尺寸:大的和小的
添加自动炮塔
可以使用子弹或 Raycast
当目标受到足够的伤害时,增加目标。
加上枪声。
要添加简单的主菜单:
带有用于更改游戏运行方式的选项菜单
带水平选择屏幕
要添加通用暂停菜单,我们可以访问任何地方
注解
虽然初学者可以完成本教程,但强烈建议您完成 你的第一场比赛 ,如果你对Godot和/或游戏开发不熟悉 之前 学习本系列教程。
记住:制作3D游戏比制作2D游戏要困难得多。如果你不知道如何制作2D游戏,你很可能在制作3D游戏时遇到困难。
本教程假设您具有使用Godot编辑器的经验、gdscript的基本编程经验以及游戏开发的基本经验。
您可以在此处找到本教程的起始资源: Godot_FPS_Starter.zip
提供的初学者资源包含一个动画3D模型、一组用于制作级别的3D模型,以及一些已经为本教程配置的场景。
提供的所有资产(除非另有说明)最初由TwistedTwigleg创建,并由Godot社区进行更改/添加。为本教程提供的所有原始资源都在 MIT
许可证。
您可以随意使用这些资产!所有原始资产属于Godot社区,其他资产属于下列资产:
注解
SkyBox是由 StumpyStrust 关于OpenGameArt。使用的Skybox的许可证如下 CC0
.
使用的字体是 Titillium-Regular ,并根据 SIL Open Font License, Version 1.1
.
小技巧
您可以在每个部分页面的底部找到每个部分的已完成项目。
零件概述¶
在这一部分中,我们将创造一个可以在环境中移动的第一人称玩家。
在这一部分的结尾,你将有一个工作的第一人称角色,他可以在游戏环境中四处移动,冲刺,用基于鼠标的第一人称摄像头环顾四周,跳入空中,打开和关闭闪光灯。
准备好一切¶
启动godot并打开包含在starter资产中的项目。
注解
虽然这些资源不一定需要使用本教程中提供的脚本,但它们将使教程更容易执行,因为在整个教程系列中我们将使用几个预设置场景。
首先,打开项目设置并转到“输入地图”选项卡。您将发现已经定义了几个操作。我们将为我们的玩家使用这些动作。如果需要,可以随意更改绑定到这些操作的键。
让我们花一点时间看看我们在初始资产中有什么。
starter资产中包括几个场景。例如,在 res://
我们有14个场景,其中大部分将在本教程系列中访问。
现在让我们打开天窗 Player.tscn
.
注解
里面有很多场景和一些纹理 Assets
文件夹。如果你愿意的话,你可以看看这些,但我们不会去探索 Assets
在本教程系列中。 Assets
包含用于每个级别的所有模型,以及一些纹理和材质。
制作FPS运动逻辑¶
一旦你拥有 Player.tscn
打开,让我们快速看一下它是如何设置的
首先,注意如何设置玩家的碰撞形状。在大多数第一人称游戏中,使用垂直指向胶囊作为玩家的碰撞形状是相当常见的。
我们在球员的“脚”上加了一个小正方形,这样球员就不会觉得他们在一个点上保持平衡。
我们确实希望“脚”比胶囊底部略高,这样我们就可以翻过轻微的边缘。“脚”的位置取决于你的水平和你希望球员的感受。
注解
很多时候,当玩家走到边缘并滑下时,他们会注意到碰撞形状是圆形的。我们在胶囊底部加上小正方形,以减少边缘的滑动。
要注意的另一件事是有多少节点是 Rotation_Helper
. 这是因为 Rotation_Helper
包含要在其上旋转的所有节点 X
轴(上下)。这背后的原因是我们可以旋转 Player
上 Y
轴,和 Rotation_helper
上 X
轴。
将新脚本附加到 Player
节点并调用它 Player.gd
.
让我们通过添加移动、用鼠标环顾四周和跳跃的能力来编程我们的玩家。将以下代码添加到 Player.gd
:
extends KinematicBody
const GRAVITY = -24.8
var vel = Vector3()
const MAX_SPEED = 20
const JUMP_SPEED = 18
const ACCEL = 4.5
var dir = Vector3()
const DEACCEL= 16
const MAX_SLOPE_ANGLE = 40
var camera
var rotation_helper
var MOUSE_SENSITIVITY = 0.05
func _ready():
camera = $Rotation_Helper/Camera
rotation_helper = $Rotation_Helper
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _physics_process(delta):
process_input(delta)
process_movement(delta)
func process_input(delta):
# ----------------------------------
# Walking
dir = Vector3()
var cam_xform = camera.get_global_transform()
var input_movement_vector = Vector2()
if Input.is_action_pressed("movement_forward"):
input_movement_vector.y += 1
if Input.is_action_pressed("movement_backward"):
input_movement_vector.y -= 1
if Input.is_action_pressed("movement_left"):
input_movement_vector.x -= 1
if Input.is_action_pressed("movement_right"):
input_movement_vector.x += 1
input_movement_vector = input_movement_vector.normalized()
# Basis vectors are already normalized.
dir += -cam_xform.basis.z * input_movement_vector.y
dir += cam_xform.basis.x * input_movement_vector.x
# ----------------------------------
# ----------------------------------
# Jumping
if is_on_floor():
if Input.is_action_just_pressed("movement_jump"):
vel.y = JUMP_SPEED
# ----------------------------------
# ----------------------------------
# Capturing/Freeing the cursor
if Input.is_action_just_pressed("ui_cancel"):
if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
# ----------------------------------
func process_movement(delta):
dir.y = 0
dir = dir.normalized()
vel.y += delta * GRAVITY
var hvel = vel
hvel.y = 0
var target = dir
target *= MAX_SPEED
var accel
if dir.dot(hvel) > 0:
accel = ACCEL
else:
accel = DEACCEL
hvel = hvel.linear_interpolate(target, accel * delta)
vel.x = hvel.x
vel.z = hvel.z
vel = move_and_slide(vel, Vector3(0, 1, 0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
func _input(event):
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
rotation_helper.rotate_x(deg2rad(event.relative.y * MOUSE_SENSITIVITY))
self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1))
var camera_rot = rotation_helper.rotation_degrees
camera_rot.x = clamp(camera_rot.x, -70, 70)
rotation_helper.rotation_degrees = camera_rot
using Godot;
using System;
public class Player : KinematicBody
{
[Export]
public float Gravity = -24.8f;
[Export]
public float MaxSpeed = 20.0f;
[Export]
public float JumpSpeed = 18.0f;
[Export]
public float Accel = 4.5f;
[Export]
public float Deaccel = 16.0f;
[Export]
public float MaxSlopeAngle = 40.0f;
[Export]
public float MouseSensitivity = 0.05f;
private Vector3 _vel = new Vector3();
private Vector3 _dir = new Vector3();
private Camera _camera;
private Spatial _rotationHelper;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_camera = GetNode<Camera>("Rotation_Helper/Camera");
_rotationHelper = GetNode<Spatial>("Rotation_Helper");
Input.SetMouseMode(Input.MouseMode.Captured);
}
public override void _PhysicsProcess(float delta)
{
ProcessInput(delta);
ProcessMovement(delta);
}
private void ProcessInput(float delta)
{
// -------------------------------------------------------------------
// Walking
_dir = new Vector3();
Transform camXform = _camera.GetGlobalTransform();
Vector2 inputMovementVector = new Vector2();
if (Input.IsActionPressed("movement_forward"))
inputMovementVector.y += 1;
if (Input.IsActionPressed("movement_backward"))
inputMovementVector.y -= 1;
if (Input.IsActionPressed("movement_left"))
inputMovementVector.x -= 1;
if (Input.IsActionPressed("movement_right"))
inputMovementVector.x += 1;
inputMovementVector = inputMovementVector.Normalized();
// Basis vectors are already normalized.
_dir += -camXform.basis.z * inputMovementVector.y;
_dir += camXform.basis.x * inputMovementVector.x;
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Jumping
if (IsOnFloor())
{
if (Input.IsActionJustPressed("movement_jump"))
_vel.y = JumpSpeed;
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Capturing/Freeing the cursor
if (Input.IsActionJustPressed("ui_cancel"))
{
if (Input.GetMouseMode() == Input.MouseMode.Visible)
Input.SetMouseMode(Input.MouseMode.Captured);
else
Input.SetMouseMode(Input.MouseMode.Visible);
}
// -------------------------------------------------------------------
}
private void ProcessMovement(float delta)
{
_dir.y = 0;
_dir = _dir.Normalized();
_vel.y += delta * Gravity;
Vector3 hvel = _vel;
hvel.y = 0;
Vector3 target = _dir;
target *= MaxSpeed;
float accel;
if (_dir.Dot(hvel) > 0)
accel = Accel;
else
accel = Deaccel;
hvel = hvel.LinearInterpolate(target, accel * delta);
_vel.x = hvel.x;
_vel.z = hvel.z;
_vel = MoveAndSlide(_vel, new Vector3(0, 1, 0), false, 4, Mathf.Deg2Rad(MaxSlopeAngle));
}
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseMotion && Input.GetMouseMode() == Input.MouseMode.Captured)
{
InputEventMouseMotion mouseEvent = @event as InputEventMouseMotion;
_rotationHelper.RotateX(Mathf.Deg2Rad(mouseEvent.Relative.y * MouseSensitivity));
RotateY(Mathf.Deg2Rad(-mouseEvent.Relative.x * MouseSensitivity));
Vector3 cameraRot = _rotationHelper.RotationDegrees;
cameraRot.x = Mathf.Clamp(cameraRot.x, -70, 70);
_rotationHelper.RotationDegrees = cameraRot;
}
}
}
这是很多代码,所以让我们按函数对其进行分解:
小技巧
尽管复制和粘贴代码是不明智的,但由于您可以从手动键入代码中学到很多东西,因此您可以直接将此页中的代码复制和粘贴到脚本编辑器中。
如果这样做,复制的所有代码都将使用空格而不是制表符。
要在脚本编辑器中将空格转换为选项卡,请单击“编辑”菜单,然后选择“将缩进转换为选项卡”。这将把所有的空格转换成制表符。您可以选择“将缩进转换为空格”将制表符转换回空格。
首先,我们定义一些类变量来指示玩家在世界上的移动方式。
注解
在本教程中, 函数外部定义的变量称为“类变量”。 . 这是因为我们可以从脚本的任何地方访问这些变量中的任何一个。
让我们检查一下每个类变量:
GRAVITY
重力有多大,把我们拉下来。vel
我们的 KinematicBody 的速度。MAX_SPEED
:我们能达到的最快速度。一旦达到这个速度,我们就不能再快了。JUMP_SPEED
我们能跳多高。ACCEL
我们加速的速度有多快。值越高,我们越快达到最大速度。DEACCEL
我们要减速多快。价值越高,我们就越快完全停止。MAX_SLOPE_ANGLE
:最陡的角度 KinematicBody 将被视为“楼层”。camera
: Camera 节点。rotation_helper
答: Spatial 节点,保存我们想要在X轴上旋转的所有内容(向上和向下)。MOUSE_SENSITIVITY
:鼠标有多灵敏。我发现一个价值0.05
适用于我的鼠标,但您可能需要根据鼠标的敏感度进行更改。
您可以调整这些变量中的许多以获得不同的结果。例如,通过降低 GRAVITY
和/或增加 JUMP_SPEED
你可以得到一个更“飘逸”的感觉。请随意尝试!
注解
你可能已经注意到了 MOUSE_SENSITIVITY
和其他常量一样,都是用大写字母写的,但是 MOUSE_SENSITIVITY
不是常量。
这背后的原因是我们希望在整个脚本中将其视为一个常量变量(一个不能更改的变量),但是我们希望在以后添加自定义设置时能够更改该值。所以,为了提醒我们自己把它当作一个常数来对待,它在所有的大写字母中都有名字。
现在让我们看看 _ready
功能:
首先我们得到 camera
和 rotation_helper
节点并将其存储到变量中。
然后我们需要将鼠标模式设置为捕获,这样鼠标就不能离开游戏窗口。
这将隐藏鼠标并将其保持在屏幕中央。我们这样做有两个原因:第一个原因是我们不希望玩家在游戏中看到他们的鼠标光标。
第二个原因是我们不希望光标离开游戏窗口。如果光标离开游戏窗口,可能会出现玩家在窗口外单击的情况,然后游戏将失去焦点。为了确保这两个问题都不会发生,我们捕获鼠标光标。
注解
看见 Input documentation 对于各种鼠标模式。我们只会用 MOUSE_MODE_CAPTURED
和 MOUSE_MODE_VISIBLE
在本教程系列中。
下一步让我们看看 _physics_process
:
我们所做的一切 _physics_process
正在调用两个函数: process_input
和 process_movement
.
process_input
将是我们存储与玩家输入相关的所有代码的地方。我们想先打电话给它,然后再打其他电话,所以我们有新的玩家输入要处理。
process_movement
我们将把所有必要的数据发送到 KinematicBody 所以它可以在游戏世界中移动。
让我们来看一看 process_input
下一步:
我们先出发 dir
到一个空的 Vector3 .
dir
将用于存储玩家想要移动的方向。因为我们不想让玩家的前一个输入影响单个玩家以外的其他玩家 process_movement
呼叫,我们重置 dir
.
Next we get the camera's global transform and store it as well, into the cam_xform
variable.
我们需要相机的全局变换的原因是我们可以使用它的方向向量。许多人发现方向向量令人困惑,所以让我们花点时间来解释它们是如何工作的:
世界空间可以定义为:相对于一个恒定的原点,所有对象都被放置在其中的空间。每一个物体,无论是二维的还是三维的,都在世界空间中占有一定的位置。
换一种说法:世界空间是宇宙中的一个空间,每个物体的位置、旋转和尺度都可以通过一个已知的固定点(即原点)来测量。
在Godot,原点在 (0, 0, 0)
旋转 (0, 0, 0)
以及 (1, 1, 1)
.
注解
打开Godot编辑器并选择 Spatial 基于节点,弹出一个小控件。默认情况下,每个箭头都使用世界空间方向。
如果你想使用世界空间方向向量移动,你可以这样做:
if Input.is_action_pressed("movement_forward"):
node.translate(Vector3(0, 0, 1))
if Input.is_action_pressed("movement_backward"):
node.translate(Vector3(0, 0, -1))
if Input.is_action_pressed("movement_left"):
node.translate(Vector3(1, 0, 0))
if Input.is_action_pressed("movement_right"):
node.translate(Vector3(-1, 0, 0))
if (Input.IsActionPressed("movement_forward"))
node.Translate(new Vector3(0, 0, 1));
if (Input.IsActionPressed("movement_backward"))
node.Translate(new Vector3(0, 0, -1));
if (Input.IsActionPressed("movement_left"))
node.Translate(new Vector3(1, 0, 0));
if (Input.IsActionPressed("movement_right"))
node.Translate(new Vector3(-1, 0, 0));
注解
注意我们不需要做任何计算来获得世界空间方向向量。我们可以定义一些 Vector3 变量并输入指向每个方向的值。
以下是二维世界空间的样子:
注解
以下图片仅为示例。每个箭头/矩形表示一个方向向量
以下是3D的外观:
请注意,在这两个示例中,节点的旋转不会更改方向箭头。这是因为世界空间是一个常数。无论您如何翻译、旋转或缩放对象,世界空间都将 始终指向同一方向 .
局部空间是不同的,因为它考虑了对象的旋转。
局部空间可以定义为:物体位置是宇宙起源的空间。因为原点的位置可以是 N
许多位置,从局部空间导出的值随原点的位置而变化。
注解
这个堆栈溢出问题对世界空间和局部空间有更好的解释。
https://gamedev.stackexchange.com/questions/65783/what-are-world-space-and-eye-space-in-game-development (Local space and eye space are essentially the same thing in this context)
得到一个 Spatial 节点的局部空间,我们需要 Transform ,这样我们就可以得到 Basis 从 Transform .
各 Basis 有三个向量: X
, Y
和 Z
. 每个向量都指向来自该对象的每个局部空间向量。
使用 Spatial 节点的局部方向向量,我们使用以下代码:
if Input.is_action_pressed("movement_forward"):
node.translate(node.global_transform.basis.z.normalized())
if Input.is_action_pressed("movement_backward"):
node.translate(-node.global_transform.basis.z.normalized())
if Input.is_action_pressed("movement_left"):
node.translate(node.global_transform.basis.x.normalized())
if Input.is_action_pressed("movement_right"):
node.translate(-node.global_transform.basis.x.normalized())
if (Input.IsActionPressed("movement_forward"))
node.Translate(node.GlobalTransform.basis.z.Normalized());
if (Input.IsActionPressed("movement_backward"))
node.Translate(-node.GlobalTransform.basis.z.Normalized());
if (Input.IsActionPressed("movement_left"))
node.Translate(node.GlobalTransform.basis.x.Normalized());
if (Input.IsActionPressed("movement_right"))
node.Translate(-node.GlobalTransform.basis.x.Normalized());
以下是2D中的局部空间:
以下是3D的外观:
这是什么 Spatial Gizmo在使用本地空间模式时显示。请注意箭头是如何跟随对象在左侧的旋转的,这看起来与局部空间的3D示例完全相同。
注解
当您有一个 Spatial 已选择基于节点。
即使对于经验丰富的游戏开发人员来说,本地向量也会令人困惑,因此不要担心这一切是否没有多大意义。关于局部向量要记住的关键是,我们使用局部坐标从对象的视角获取方向,而不是使用从世界的视角给出方向的世界向量。
好的,回到 process_input
:
接下来我们将生成一个名为 input_movement_vector
把它分配给一个空的 Vector2 . 我们将使用它来创建一个虚拟轴,将玩家的输入映射到动作。
注解
对于键盘来说,这似乎有些杀伤力,但当我们稍后添加操纵手柄输入时,这将是合理的。
根据所按下的方向运动动作,我们加上或减去 input_movement_vector
.
在我们检查了每一个定向运动动作后,我们将正常化 input_movement_vector
. 这使得它在哪里 input_movement_vector
的值在 1
半径单位圆。
接下来我们添加相机的本地 Z
矢量次数 input_movement_vector.y
到 dir
. 这样,当播放器向前或向后按时,我们会添加相机的本地 Z
轴,使播放器相对于相机向前或向后移动。
注解
因为摄像机是由 -180
度,我们必须翻转 Z
方向向量。通常向前是正z轴,因此使用 basis.z.normalized()
可以,但我们正在使用 -basis.z.normalized()
因为我们相机的Z轴相对于其他播放器向后。
我们对摄像机的本地设备做同样的事情 X
矢量,而不是使用 input_movement_vector.y
我们用 input_movement_vector.x
. 这样,当播放机按下左/右键时,就可以相对于相机向左/向右移动。
接下来,我们用 KinematicBody 的 is_on_floor
功能。如果是,那么我们检查一下“移动跳跃”动作是否刚刚被按下。如果有的话,我们就设置玩家的 Y
速度到 JUMP_SPEED
.
因为我们设定的是Y速度,所以玩家会跳到空中。
然后我们检查 ui_cancel
行动。这样我们就可以在 escape
按钮被按下。我们这样做是因为否则将无法释放光标,这意味着在您终止运行时之前,光标将被卡住。
要释放/捕获光标,我们检查鼠标是否可见(释放)。如果是,我们会捕捉它,如果不是,我们会让它可见(释放它)。
这就是我们现在所做的一切 process_input
. 当我们为玩家增加更多的复杂性时,我们将多次回到这个功能中。
现在让我们看看 process_movement
:
首先,我们保证 dir
上没有任何移动 Y
通过设置轴 Y
值为零。
接下来我们规范化 dir
确保我们在 1
半径单位圆。这使得我们以恒定的速度移动,不管玩家是直线移动还是对角线移动。如果我们没有正常化,球员在对角线上的移动速度会比直行时快。
接下来,我们通过添加 GRAVITY * delta
到球员的 Y
速度。
之后,我们将玩家的速度分配给一个新的变量(称为 hvel
)并移除 Y
轴。
接下来我们设置一个新变量 (target
)到玩家的方向向量。然后,我们将其乘以玩家的最大速度,这样我们就知道玩家将向提供的方向移动多远。 dir
.
在这之后,我们为加速度做了一个新的变量,命名为 accel
.
然后我们得到的点积 hvel
看球员是否按照 hvel
. 记得, hvel
没有 Y
速度,这意味着我们只检查玩家是否向前、向后、向左或向右移动。
如果玩家按照 hvel
,然后我们开始 accel
到 ACCEL
常数,这样玩家将加速,否则我们设置 accel
对我们 DEACCEL
保持不变,以便玩家减速。
然后我们插入水平速度,设置玩家的 X
和 Z
速度到内插水平速度,并调用 move_and_slide
让 KinematicBody 在物理世界中移动玩家。
小技巧
所有的代码 process_movement
与运动角色演示中的运动代码完全相同!
我们的最终功能是 _input
功能,谢天谢地,它相当短:
首先,我们要确保我们正在处理的事件是 InputEventMouseMotion 事件。我们还想检查光标是否被捕获,因为如果没有,我们不想旋转。
注解
见 Mouse and input coordinates 有关可能的输入事件的列表。
如果事件确实是鼠标运动事件,并且捕获了光标,我们将根据提供的鼠标相对运动进行旋转。 InputEventMouseMotion .
首先我们旋转 rotation_helper
上的节点 X
轴,使用相对鼠标运动的 Y
值,由提供 InputEventMouseMotion .
然后我们旋转整个 KinematicBody 上 Y
鼠标相对运动的轴 X
价值。
小技巧
Godot将相对鼠标运动转换为 Vector2 鼠标上下移动的位置 1
和 -1
分别。左右移动是 1
和 -1
分别。
因为我们是如何旋转播放器的,所以我们将鼠标的相对运动乘以 X
价值依据 -1
所以鼠标左、右移动会使播放器在同一方向上左右旋转。
最后,我们夹紧 rotation_helper
的 X
旋转介于 -70
和 70
度,这样玩家就不能自上而下旋转。
小技巧
见 using transforms 有关旋转变换的详细信息。
要测试代码,请打开名为 Testing_Area.tscn
如果还没有打开。在接下来的几个教程部分中,我们将使用这个场景,因此请确保在其中一个场景选项卡中打开它。
继续测试您的代码,按 F6
具有 Testing_Area.tscn
作为“打开”选项卡,按右上角的“播放”按钮或按 F5
. 你现在应该可以四处走动,跳到空中,用鼠标环顾四周。
给玩家一个闪光灯和选择冲刺¶
在我们开始制造武器之前,我们还需要补充一些东西。
很多fps游戏都有冲刺和手电筒的选择。我们可以很容易地将这些添加到我们的玩家中,所以我们开始吧!
首先,我们在播放器脚本中需要更多的类变量:
const MAX_SPRINT_SPEED = 30
const SPRINT_ACCEL = 18
var is_sprinting = false
var flashlight
[Export]
public float MaxSprintSpeed = 30.0f;
[Export]
public float SprintAccel = 18.0f;
private bool _isSprinting = false;
private SpotLight _flashlight;
所有短跑变量的工作方式与具有相似名称的非短跑变量完全相同。
is_sprinting
是一个用于跟踪玩家当前是否正在冲刺的布尔值,以及 flashlight
是一个变量,我们将用来保存播放器的闪光灯节点。
现在我们需要添加几行代码,从 _ready
. 将以下内容添加到 _ready
:
flashlight = $Rotation_Helper/Flashlight
_flashlight = GetNode<SpotLight>("Rotation_Helper/Flashlight");
This gets the Flashlight
node and assigns it to the flashlight
variable.
现在我们需要更改 process_input
. 在中的某个位置添加以下内容 process_input
:
# ----------------------------------
# Sprinting
if Input.is_action_pressed("movement_sprint"):
is_sprinting = true
else:
is_sprinting = false
# ----------------------------------
# ----------------------------------
# Turning the flashlight on/off
if Input.is_action_just_pressed("flashlight"):
if flashlight.is_visible_in_tree():
flashlight.hide()
else:
flashlight.show()
# ----------------------------------
// -------------------------------------------------------------------
// Sprinting
if (Input.IsActionPressed("movement_sprint"))
_isSprinting = true;
else
_isSprinting = false;
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Turning the flashlight on/off
if (Input.IsActionJustPressed("flashlight"))
{
if (_flashlight.IsVisibleInTree())
_flashlight.Hide();
else
_flashlight.Show();
}
让我们看一下附加的内容:
We set is_sprinting
to true when the player is holding down the movement_sprint
action, and false
when the movement_sprint
action is released. In process_movement
we'll add the code that makes the player faster when
they sprint. Here in process_input
we are just going to change the is_sprinting
variable.
我们所做的类似于释放/捕获光标以处理手电筒。我们首先检查一下 flashlight
行动刚刚被按下。如果是的话,我们会检查一下 flashlight
在场景树中可见。如果是,那我们就把它藏起来,如果不是,我们就把它展示出来。
现在我们需要在 process_movement
. 首先,更换 target *= MAX_SPEED
包括以下内容:
if is_sprinting:
target *= MAX_SPRINT_SPEED
else:
target *= MAX_SPEED
if (_isSprinting)
target *= MaxSprintSpeed;
else
target *= MaxSpeed;
现在,而不是一直乘以 target
通过 MAX_SPEED
首先,我们检查球员是否正在冲刺。如果运动员在短跑,我们将增加 target
通过 MAX_SPRINT_SPEED
.
现在剩下的就是改变冲刺时的加速度。变化 accel = ACCEL
致:
if is_sprinting:
accel = SPRINT_ACCEL
else:
accel = ACCEL
if (_isSprinting)
accel = SprintAccel;
else
accel = Accel;
现在,当运动员冲刺时,我们将使用 SPRINT_ACCEL
而不是 ACCEL
这将加快玩家的速度。
如果按 shift
按钮,可以通过按 F
按钮!
去试试看!您可以更改与sprint相关的类变量,使运动员在短跑时更快或更慢!