[unity-FPS学习1]关于unity人物的移动脚本

目录

一、部分知识:

1.RequireComponent类方法:

 2.ToolTip特性:

3.[HideInInspector]:

4.[Header("xxx")]:该代码用于在Inspector面板上给之后定义的puclic变量加一个标题.

5.Awake():

6.FixedUpdate():

7.velocity(速度):

8.Vector3.SmoothDamp():

9.AddForce():

10.AddRelativeForce():

11.Input.GetAxis(""):

 12.AudioSource组件:

13.Layer相关知识

14.ref 和out

二、核心脚本实现

(1)OnCollisionStay();是在①两者之间处在碰撞状态下,②两者之间有相对移动的情况下才触发,用于检测 进入、保持和退出状态。

(2)Vector2.Angle;

(1)RaycastHit(存储射线投射操作):

(2)Physics.Raycast:是 Unity 中用于检测物体之间碰撞的函数之一。它使用一条射线来检测场景中的物体,返回一个 bool 值表示是否检测到了碰撞,以及一个 RaycastHit 结构体存储着射线碰撞到的物体的信息。

(1)Transform.localScale控制的对象即为在Inspector面板下看到的一个物体的Transform组件中的Scale选项,这是一个vector3类的变量,说明其有x、y、z三个变量。

(2)Vector3.Lerp ()用于在两个三维向量之间进行线性插值。它的作用是根据给定的插值因子(通常是一个介于0和1之间的值)来计算两个向量之间的中间值。
​​​​​​​


我们要想实现人物的移动,必须去通过C#脚本去实现,下面是一些知识:

一、部分知识:

1.RequireComponent类方法:

挂载组件给物体

[RequireComponent(typeof(Rigidbody))]

 2.ToolTip特性:

类似于代码的注释功能,用于inspecter面板。

添加一个tooltip属性可以在instpector面板上提示注释

	[Tooltip("Current players speed")]
    //注释:当下玩家的速度,会在inspecter面板上显示"Current players speed"
    public float currentSpeed;
    //接受注释的部分

效果:

3.[HideInInspector]:

顾名思义,隐藏在检查器中(inspecter),因为Public类型的变量会被Unity自动序列化,若你定义了公有的成员变量,但不想让它显示在面板上面,可以这样做:

[HideInInspector]public Transform cameraMain;

或是:

[HideInInspector]public int m_num=0;

会使他们不会再inspecter面板上显示出来;

4.[Header("xxx")]:
该代码用于在Inspector面板上给之后定义的puclic变量加一个标题.

[Header("Player SOUNDS")]
[Tooltip("Jump sound when player jumps.")]
public AudioSource _jumpSound;

5.Awake():

生命周期函数中的唤醒事件,一开始就执行并且只执行一次;

6.FixedUpdate():

生命周期函数中的固定更新事件,每0.02s执行一次(可人为修改),不受帧率的影响;

Edit–>Project Settings–>Time 面板中的Fixed Timestep参数设置。

7.velocity(速度):

Rigidbody.velocity是一个3D向量,包含了刚体在x、y和z轴上的速度分量。velocity的单位是米每秒,而不是帧每秒,其中米是Unity默认的长度单位。

magnitude(大小) 返回向量的长度,也就是点P(x,y,z)到原点(0,0,0)的距离。 最常用的是用来返回物体的移动速度,比如:

speed=rigidbody.velocity.magnitude;

用来返回一个物体的速度,在Unity手册中这样描述:

8.Vector3.SmoothDamp():

随时间推移将一个值逐渐改变为所需目标。

值通过某个类似于弹簧-阻尼的函数(它从不超过目标)进行平滑。 该函数可以用于平滑任何类型的值、位置、颜色、标量。

9.AddForce():

原文链接:Unity3D入门(九):刚体常用方法介绍_fixed timestep参数-CSDN博客

给刚体添加一个力,让刚体按“世界坐标系”进行运动。例如,游戏中发射的子弹

Rigidbody.AddForce(Vector3,ForceMode);

Vector3:力的方向和大小;
ForceMode:力的模式[enum 类型]。

ForceMode 参数:
类型为枚举类型,以什么样的方式添加力给刚体。

枚举值说明
Acceleration加速度
Force力,这种模式通常用于设置真实的物理,最常用
Impulse冲击力,这种模式通常用于瞬间发生的力
VelocityChange速度的变化

10.AddRelativeForce():

给刚体添加一个力,让刚体按“自身坐标系”进行运动。

Rigidbody.AddRelativeForce(Vector3,ForceMode);

Vector3:力的方向和大小;
ForceMode:力的模式[enum 类型]。

有时候会出现三个值的情况,追踪进去它长这样:

    public void AddRelativeForce(float x, float y, float z)
    {
        AddRelativeForce(new Vector3(x, y, z), ForceMode.Force);
    }

11.Input.GetAxis(""):

Unity 引擎中的一个方法,用于获取游戏玩家在键盘或游戏手柄上输入的某个轴(Axis)的值。这里的 "" 是一个字符串参数,表示要获取的轴的名称。

在 Unity 中,有多种轴类型,如 "Horizontal"(水平轴)、"Vertical"(垂直轴)、"Mouse X"(鼠标水平滚轮轴)和 "Mouse Y"(鼠标垂直滚轮轴)等。我们可以通过

float Input_axis=Input.GetAxis("Horizontal");
Debug.Log(Input_axis);

来检测该值

 12.AudioSource组件:

 这里我看了一个博主的文章写的很详细,附上地址:

原文链接:https://blog.csdn.net/sunhao521111/article/details/118958710

AudioSource是音频源组件,其作用就是用于播放音频剪辑(AudioClip)资源。

2. 组件属性
(1)AudioClip(音频剪辑):指定播放的音频文件。

(2)Output(音频输出):可以输出到音频监听器(AudioListener)或者(AudioMixer)。当设置为空时,即代表输出到 AudioListener,而设置到AudioMixer时需要指定具体的AudioMixer。

(3)Mute(是否静音):主要是针对游戏中的音效,优势在于不会卸载声音数据,可以做到及时播放,音效一般比较多、占用内存小,使用静音可以让画面快速响应,且可以立刻恢复当前音效。

(4)Bypass Effects(音源滤波开关):作用在当前音源的音频滤波器的开关。

(5)Bypass Listener Effects(监听器滤波开关):作用在当前监听器的音频滤波器的开关。

(6)Bypass Reverb Zones(回音混淆开关):勾选不执行回音混淆的效果。

(7)Play On Awake(启动播放开关):勾选就会在绑定组件的GameObject加载并启用时立刻播放音频。

(8)Loop(循环播放开关):音频播放结束自动循环。

(9)Priority(播放优先级):决定音源在场景中存在的所有音源中的播放优先级。

(10)Volume(音量):调节音量的大小。

(11)Pitch(音调):播放音频时速度的变化量 ,默认值1,表示正常的播放速度。(当<1时,慢速播放;当>1时,快速播放。速度越快,音调越高。)

(12)Stereo Pan(声道占比)

(13)Spatial Blend(空间混合):指定音源是2D音源(0)、3D音源(1)或者是二者插值的复合音源。

(14)Reverb Zone Mix(回音混合)

(15)3D Sound Settings(3D音频设置)

13.Layer相关知识

我看的是这个博主的文章,很详细

Unity 基础 之 Layer(层layer) 、LayerMask (遮罩层) 的 总结 和 使用(CullingMask、Ray 射线的使用等)_unity layermask-CSDN博客

例如;LayerMask mask = 1 << 3;表示开启Layer3。

1 << 3这个表达式的含义是将数字1左移3位。在二进制中,左移操作相当于乘以2的幂次方。因此,1 << 3等于8(即二进制的1000)。这意味着我们创建了一个LayerMask,其中只有第4个位(从右往左数,从0开始计数)是1,其他位都是0。

当我们将LayerMask设置为1 << 3时,实际上是开启了Layer 3。因为在Unity中,LayerMask的值是从0到31,分别对应于Layer 0到Layer 31。所以,Layer 3对应的位就是第4位(从右往左数),而Layer 3对应的LayerMask值就是1 << 3

使用层的名称设置,例如:

LayerMask mask  = 1 << LayerMask.NameToLayer(“TestLayer”);  表示开启层名“TestLayer” 的层 

LayerMask mask  = 0 << LayerMask.NameToLayer(“TestLayer”);  表示关闭层名“TestLayer” 的层 

14.ref 和out

这部分可以参考Unity ref 和out、 params的使用-CSDN博客

二、核心脚本实现

现在,我们开始脚本的编辑:

首先,我们挂载刚体组件

[RequireComponent(typeof(Rigidbody))]

接着在内部

	Rigidbody rb;

	[Tooltip("Current players speed")]
    //注释:当下玩家的速度,会在inspecter面板上显示"Current players speed"
    public float currentSpeed;//接受注释的部分
	[Tooltip("Assign players camera here")]
    //在这里获取玩家的摄像机
	[HideInInspector]public Transform cameraMain;
	[Tooltip("Force that moves player into jump")]
    //使移动中的玩家跳跃
	public float jumpForce = 500;
    //跳跃的力为500
	[Tooltip("Position of the camera inside the player")]
    //摄像机在玩家体内的位置
	[HideInInspector]public Vector3 cameraPosition;
void Awake(){
	rb = GetComponent<Rigidbody>();
	cameraMain = transform.Find("Main Camera").transform;
	bulletSpawn = cameraMain.Find ("BulletSpawn").transform;
	ignoreLayer = 1 << LayerMask.NameToLayer ("Player");

}

 Awake函数中第一行我们获取了玩家的刚体组件,第二行查找当前游戏对象下的名为"Main Camera"的子对象,并将其Transform组件赋值给变量cameraMain。,第三行获取了场景中一个BulletSpawn(子弹生成位置)的物件,第四行将ignoreLayer设置为“Player”图层。

void PlayerMovementLogic(){
	currentSpeed = rb.velocity.magnitude;
	horizontalMovement = new Vector2 (rb.velocity.x, rb.velocity.z);
	if (horizontalMovement.magnitude > maxSpeed){
		horizontalMovement = horizontalMovement.normalized;//归一化处理
		horizontalMovement *= maxSpeed;    
	}
    //处理平面速度
	rb.velocity = new Vector3 (
		horizontalMovement.x,
		rb.velocity.y,
		horizontalMovement.y
	);
	if (grounded){
		rb.velocity = Vector3.SmoothDamp(rb.velocity/*当前位置*/,
			new Vector3(0,rb.velocity.y,0)/*目标位置*/,
			ref slowdownV/*当前速度*/,
			deaccelerationSpeed/*减速度*/);
	}

	if (grounded) {
		rb.AddRelativeForce (Input.GetAxis ("Horizontal") * accelerationSpeed/*加速度*/ * Time.deltaTime, 0, Input.GetAxis ("Vertical") * accelerationSpeed * Time.deltaTime);
	} else {
		rb.AddRelativeForce (Input.GetAxis ("Horizontal") * accelerationSpeed / 2 * Time.deltaTime/*水平速度减半*/, 0, Input.GetAxis ("Vertical") * accelerationSpeed / 2 * Time.deltaTime/*垂直速度减半*/);
      //减半了空中运动速度
	}
    /*
	 * 此处修复了滑溜溜的问题
	 */
    if (Input.GetAxis ("Horizontal") != 0 || Input.GetAxis ("Vertical") != 0) {
		deaccelerationSpeed = 0.5f;
	} else {
		deaccelerationSpeed = 0.1f;
	}
}

这里我们处理了玩家的移动逻辑,我们先设置了当前的移动速度为rb.velocity.magnitude(刚体移动速度大小),由于平面只有x、z轴,所以我们将平面移动用一个二维向量表示。

然后进入if判断平面移动的速度是否大于最大速度,如果比最大速度大,我们就将其归一化处理后,再处理后的值乘最大速度再赋给平面移动速度。随后将处理后的x、y、z轴伤的速度赋给刚体速度(Vector3类型)。

然后我们再判断玩家是否在地面上时移动的两种状况,这里我理解的应该是平滑处理,即平滑地减速游戏对象的运动

接着通过一个条件判断语句,来调整游戏对象的减速速度。当玩家有水平或垂直方向的输入时,将减速速度设置为0.5f;否则,将减速速度设置为0.1f。这样可以使得当玩家停止输入时,游戏对象能够更快地减速。

	[Tooltip("The maximum speed you want to achieve")]//您想要达到的最大速度
    public int maxSpeed = 5;
	[Tooltip("The higher the number the faster it will stop")]//数字越大,它停止的速度越快
    public float deaccelerationSpeed = 15.0f;


	[Tooltip("Force that is applied when moving forward or backward")]//向前或向后移动时施加的力
    public float accelerationSpeed = 50000.0f;

这里我们设置最大速度maxSpeed和减速度与加速度的值

	[Tooltip("Tells us weather the player is grounded or not.")]//告诉我们玩家是否在地面上
	public bool grounded;
    /*
	* 检查我们的玩家是否以小于 60 度的角度接触地面
	*	如果是,将 Groudede 设置为 true
	*/
    void OnCollisionStay(Collision other){
		foreach(ContactPoint contact in other.contacts){
			if(Vector2.Angle(contact.normal,Vector3.up) < 60){
				grounded = true;
			}
		}
	}
    /*
	* 当碰撞体离开时,将grounded设置为 false
	*/
    void OnCollisionExit ()
	{
		grounded = false;
	}

这里我们定义一个bool类型的值grounded,与上面的if判断中的grounded对应,同时在此处判断玩家是否以小于60度的角度接触地面,如果是,就将grounded设置为true

这里补充一点知识:

(1)OnCollisionStay();
是在①两者之间处在碰撞状态下,②两者之间有相对移动的情况下才触发,用于检测 进入、保持和退出状态。
(2)Vector2.Angle;

是Unity中的一个静态方法,用于计算两个二维向量之间的角度。它接受两个参数:第一个参数是参考向量(通常是向上方向或其他基准向量),第二个参数是要计算角度的目标向量。

该方法返回一个浮点数,表示两个向量之间的角度(以度为单位)。如果目标向量与参考向量之间的夹角小于180度,则返回正值;如果大于180度,则返回负值。

回过来,我们这里接着通过OnCollisionStay()方法和foreach遍历每一个碰撞点的信息,判断每一个碰撞点的法线向量与向上方向(Vector3.up)之间的角度是否小于60度,来判断玩家是否站在地面上。

接着通过OnCollisionExit ()方法判断玩家离开地面,并将grounded更新为false;

	void Jumping(){
		if (Input.GetKeyDown (KeyCode.Space) && grounded) {
			rb.AddRelativeForce (Vector3.up * jumpForce);
			if (_jumpSound)
				_jumpSound.Play ();
			else
				print ("Missig jump sound.");
			_walkSound.Stop ();
			_runSound.Stop ();
		}
	}

这里进行跳跃逻辑的控制,首先if判断是否获取到键盘输入空格同时玩家处于地面上,若满足条件,则继续给刚体添加向上的力,随后继续if判断如果存在跳跃音效(_jumpSound),则播放该音效。否则输出提示信息,同时走路音效和跑步音效停止。

注意:这里的_jumpSound是一个AudioSource类型,后面会提到,可以用于判断语句,          若 _jumpSound是一个有效的 AudioSource 对象(即不为 null),那么这个条件判断会返回 true,否则返回 false。

	private bool RayCastGrounded(){
		RaycastHit groundedInfo;
		if(Physics.Raycast(transform.position/*射线的起点位置,即角色的位置*/, transform.up *-1f/*射线的方向,向下(因为乘以 - 1)*/, out groundedInfo/*将射线检测的结果存储在groundedInfo变量中*/, 1/*射线的长度*/, ~ignoreLayer/*射线检测时忽略的层级*/))/*判断射线是否检测到地面*/{
			Debug.DrawRay (transform.position/*射线起始位置*/, transform.up * -1f, Color.red, 0.0f);
			if(groundedInfo.transform != null){
                //如果存在,返回true,表示角色在地面上。
                return true;
			}
			else{
                //如果不存在,返回false,表示角色不在地面上。
				return false;
			}
		}
        //如果射线没有检测到地面,直接返回false,表示角色不在地面上。

        return false;
	}

这里我们对玩家是否再地面上进行第二次检验,光线投射下来检查我们是否沿着 gorunded 方法接地,因为如果地板是弯曲的,它会持续打开/关闭,这向我们保证我们是否真的接地。

补充知识:

(1)RaycastHit(存储射线投射操作):

可以参考这篇博客,就不列出来了Unity 之 RaycastHit(存储射线投射操作)_unity raycasthit-CSDN博客

(2)Physics.Raycast:​​​​​​​是 Unity 中用于检测物体之间碰撞的函数之一。它使用一条射线来检测场景中的物体,返回一个 bool 值表示是否检测到了碰撞,以及一个 RaycastHit 结构体存储着射线碰撞到的物体的信息。

参考博客:Physics.Raycast-CSDN博客

回过来,我们创建一个bool类型的光线射线地面判定方法,先定义一个名为groundedInfo的变量,用于存储射线检测的结果。

随后进入if判断射线是否检测到了地面,在这里我们把Physics.Raycast中的内容展开来分析:

  • transform.position:射线的起点位置,即角色的位置。
  • transform.up * -1f:射线的方向,向下(因为乘以-1)。
  • out groundedInfo:将射线检测的结果存储在groundedInfo变量中。
  • 1:射线的长度,这里设置为1米。
  • ~ignoreLayer:射线检测时忽略的层级,~表示按位取反,即不检测这些层级。

如果射线检测到地面(即满足上述条件),则执行以下操作:

  • Debug.DrawRay(transform.position, transform.up * -1f, Color.red, 0.0f);:在场景中绘制一条红色的射线,用于调试。
  • if(groundedInfo.transform != null):检查射线检测到的对象是否存在(即是否有碰撞)。
    • 如果存在,返回true,表示角色在地面上。
    • 如果不存在,返回false,表示角色不在地面上。

如果在第一步(第一个if判断处)射线没有检测到地面,直接返回false,表示角色不在地面上。

    void WalkingSound(){
		if (_walkSound && _runSound) {
			if (RayCastGrounded ())
            { //对于步行音效使用它,因为表面不一定是直的			
                if (currentSpeed > 1) {
					//				
					if (maxSpeed == 3) {
						//	
						if (!_walkSound.isPlaying) {
							//	
							_walkSound.Play ();
							_runSound.Stop ();
						}					
					} else if (maxSpeed == 5) {
						//	

						if (!_runSound.isPlaying) {
							_walkSound.Stop ();
							_runSound.Play ();
						}
					}
				} else {
					_walkSound.Stop ();
					_runSound.Stop ();
				}
			} else {
				_walkSound.Stop ();
				_runSound.Stop ();
			}
		} else {
			print ("Missing walk and running sounds.");//缺少走路和跑步的声音
		}

	}

 这里我们创建一个WalkingSound方法来控制角色行走时的声音播放,首先检查是否存在步行和跑步声音_walkSound 和 _runSound),如果不存在则打印错误信息。

如果存在步行和跑步声音,那么继续执行以下操作:

  • 使用 RayCastGrounded() 函数检测角色是否在地面上。
  • 如果角色在地面上并且当前速度大于1(currentSpeed > 1),则根据最大速度(maxSpeed)的值来决定播放哪种声音:
    • 如果最大速度为3,且步行声音没有播放,则停止跑步声音并播放步行声音。
    • 如果最大速度为5,且跑步声音没有播放,则停止步行声音并播放跑步声音。
  • 如果角色不在地面上或者当前速度不大于1,则停止所有声音。
    void Crouching()
    {//如果玩家切换蹲伏,它将缩放玩家以显示蹲伏
        if (Input.GetKey(KeyCode.C)){
			transform.localScale = Vector3.Lerp(transform.localScale/*起始向量*/, new Vector3(1,0.6f,1)/*目标向量*/, Time.deltaTime * 15/*过渡速度/程度*/);
		}
		else{
			transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(1,1,1), Time.deltaTime * 15);

		}
	}

 这里我们处理玩家的下蹲逻辑处理,在这我们使用缩放来达到想要的下蹲效果,首先检查玩家是否按下了 "C" 键,如果玩家按下了 "C" 键,那么将角色的缩放值(transform.localScale从当前值平滑过渡到一个新的缩放值(new Vector3(1,0.6f,1),这个新的缩放值表示角色在蹲伏时的尺寸。过渡的速度由 Time.deltaTime * 15 控制,其中 Time.deltaTime 是上一帧的时间间隔,乘以一个系数(这里是15)来调整过渡速度。

如果玩家没有按下 "C" 键,那么将角色的缩放值从当前值平滑过渡回原始尺寸(new Vector3(1,1,1)),同样使用 Time.deltaTime * 15 控制过渡速度。

补充知识:

(1)Transform.localScale控制的对象即为在Inspector面板下看到的一个物体的Transform组件中的Scale选项,这是一个vector3类的变量,说明其有x、y、z三个变量。

(2)Vector3.Lerp ()用于在两个三维向量之间进行线性插值。它的作用是根据给定的插值因子(通常是一个介于0和1之间的值)来计算两个向量之间的中间值。

具体来说,Vector3.Lerp 函数接受三个参数:起始向量 a、目标向量 b 和插值因子 t。插值因子 t 决定了从起始向量到目标向量的过渡程度。当 t 为0时,返回的是起始向量;当 t 为1时,返回的是目标向量;当 t 在0和1之间时,返回的是两个向量之间的一个中间值。

在这里我们Vector3.Lerp(transform.localScale, new Vector3(1,0.6f,1), Time.deltaTime * 15)后再通过else恢复,由于两个的插值因子t都相同,所以会平滑的恢复原有的缩放值,不用担心会出现缩放大小失误的问题

RaycastHit hitInfo;//存储射线检测的结果。
private float meleeAttack_cooldown;//用于存储近战攻击的冷却时间。
private string currentWeapo;//存储当前武器的名称。
[Tooltip("Put 'Player' layer here")]//将“玩家”图层放在这里
[Header("Shooting Properties")]//射击属性
private LayerMask ignoreLayer;//忽略玩家图层
Ray ray1, ray2, ray3, ray4, ray5, ray6, ray7, ray8, ray9;
private float rayDetectorMeeleSpace = 0.15f;//设置近战空间的射线探测距离
private float offsetStart = 0.05f;//设置射线检测的起始偏移量
[Tooltip("Put BulletSpawn gameobject here, palce from where bullets are created.")]//将 BulletSpawn 游戏对象放在这里,从创建子弹的位置开始。
[HideInInspector]
public Transform bulletSpawn; //存储子弹生成的位置。

 这里我们开始准备处理近战攻击和射击功能,这里主要干了这几件事:

  1. RaycastHit hitInfo;:定义一个名为hitInfo的RaycastHit变量,用于存储射线检测的结果
  2. private float meleeAttack_cooldown;:定义一个私有浮点数变量meleeAttack_cooldown,用于存储近战攻击的冷却时间
  3. private string currentWeapo;:定义一个私有字符串变量currentWeapo,用于存储当前武器的名称。
  4. [Header("Shooting Properties")]:这是一个属性标签,用于在检查器面板中创建一个标题为“Shooting Properties”的部分,用于存放射击相关的属性
  5. private LayerMask ignoreLayer;:定义一个私有的LayerMask变量ignoreLayer,用于存储需要忽略的图层(在这里是玩家图层)。
  6. Ray ray1, ray2, ray3, ray4, ray5, ray6, ray7, ray8, ray9;定义了九个Ray类型的变量。
  7. private float rayDetectorMeeleSpace = 0.15f;:定义一个私有浮点数变量 rayDetectorMeeleSpace,用于设置近战空间的射线探测距离,默认值为0.15。
  8. private float offsetStart = 0.05f;:定义一个私有浮点数变量offsetStart,用于设置射线检测的起始偏移量,默认值为0.05。
  9. [HideInInspector]:前面提到过,表示该变量在Unity检查器面板中不可见。
  10. public Transform bulletSpawn;:定义一个公共的Transform变量bulletSpawn,用于存储子弹生成的位置

    public bool been_to_meele_anim = false;//是否已经进行了近战动画
    private void RaycastForMeleeAttacks(){
        //检测近战攻击




        if (meleeAttack_cooldown > -5) {
			meleeAttack_cooldown -= 1 * Time.deltaTime;
        }//实现冷却时间的递减


        if (GetComponent<GunInventory> ().currentGun) {
			if (GetComponent<GunInventory> ().currentGun.GetComponent<GunScript> ()) 
				currentWeapo = "gun";//表示当前武器是枪械
        }
		//绘制射线
		//middle row
		ray1 = new Ray (bulletSpawn.position + (bulletSpawn.right*offsetStart), bulletSpawn.forward + (bulletSpawn.right * rayDetectorMeeleSpace));
		ray2 = new Ray (bulletSpawn.position - (bulletSpawn.right*offsetStart), bulletSpawn.forward - (bulletSpawn.right * rayDetectorMeeleSpace));
		ray3 = new Ray (bulletSpawn.position, bulletSpawn.forward);
		//upper row
		ray4 = new Ray (bulletSpawn.position + (bulletSpawn.right*offsetStart) + (bulletSpawn.up*offsetStart), bulletSpawn.forward + (bulletSpawn.right * rayDetectorMeeleSpace) + (bulletSpawn.up * rayDetectorMeeleSpace));
		ray5 = new Ray (bulletSpawn.position - (bulletSpawn.right*offsetStart) + (bulletSpawn.up*offsetStart), bulletSpawn.forward - (bulletSpawn.right * rayDetectorMeeleSpace) + (bulletSpawn.up * rayDetectorMeeleSpace));
		ray6 = new Ray (bulletSpawn.position + (bulletSpawn.up*offsetStart), bulletSpawn.forward + (bulletSpawn.up * rayDetectorMeeleSpace));
		//bottom row
		ray7 = new Ray (bulletSpawn.position + (bulletSpawn.right*offsetStart) - (bulletSpawn.up*offsetStart), bulletSpawn.forward + (bulletSpawn.right * rayDetectorMeeleSpace) - (bulletSpawn.up * rayDetectorMeeleSpace));
		ray8 = new Ray (bulletSpawn.position - (bulletSpawn.right*offsetStart) - (bulletSpawn.up*offsetStart), bulletSpawn.forward - (bulletSpawn.right * rayDetectorMeeleSpace) - (bulletSpawn.up * rayDetectorMeeleSpace));
		ray9 = new Ray (bulletSpawn.position -(bulletSpawn.up*offsetStart), bulletSpawn.forward - (bulletSpawn.up * rayDetectorMeeleSpace));

		Debug.DrawRay (ray1.origin, ray1.direction, Color.cyan);
		Debug.DrawRay (ray2.origin, ray2.direction, Color.cyan);
		Debug.DrawRay (ray3.origin, ray3.direction, Color.cyan);
		Debug.DrawRay (ray4.origin, ray4.direction, Color.red);
		Debug.DrawRay (ray5.origin, ray5.direction, Color.red);
		Debug.DrawRay (ray6.origin, ray6.direction, Color.red);
		Debug.DrawRay (ray7.origin, ray7.direction, Color.yellow);
		Debug.DrawRay (ray8.origin, ray8.direction, Color.yellow);
		Debug.DrawRay (ray9.origin, ray9.direction, Color.yellow);

		if (GetComponent<GunInventory> ().currentGun) {
			if (GetComponent<GunInventory> ().currentGun.GetComponent<GunScript> ().meeleAttack == false) /*检查当前武器是否具有近战攻击功能*/ {
				been_to_meele_anim = false;
			}
			if (GetComponent<GunInventory> ().currentGun.GetComponent<GunScript> ().meeleAttack == true && been_to_meele_anim == false) {
				been_to_meele_anim = true;
				//	if (isRunning == false) {
				StartCoroutine ("MeeleAttackWeaponHit");
				//	}
			}
		}

	}

 上面这部分首先定义了一个布尔变量been_to_meele_anim,初始值为false,表示是否已经进行了近战动画。

接下来是一个名为RaycastForMeleeAttacks的私有方法,用于检测近战攻击。在这个方法中,首先检查meleeAttack_cooldown(近战攻击冷却时间)是否大于-5,如果是,则将其减去当前时间与上次更新时间的差值(Time.deltaTime),以实现冷却时间的递减。

然后,通过调用GetComponent<GunInventory>()获取游戏中的角色的GunInventory组件,并检查其currentGun属性是否存在。如果存在,再通过调用GetComponent<GunScript>()获取当前武器的GunScript组件,并将currentWeapo变量设置为"gun",表示当前武器是枪械。注意:这里的GunInventory是另一个枪械库存的脚本,GunScript是另一个枪械的脚本,我这里没有写出来

接下来,代码绘制了9条射线,用于检测近战攻击的范围。这些射线分别位于不同的方向和距离上,以便检测潜在的敌人或障碍物。 在这里,我们详细分析一下这些射线:

  1. ray1, ray2, 和 ray3 是中间行的射线:

    • ray1的起点是bulletSpawn的位置向右偏移offsetStart的距离,方向是bulletSpawn的朝向向量加上向右偏移rayDetectorMeeleSpace的距离。
    • ray2的起点是bulletSpawn的位置向左偏移offsetStart的距离,方向是bulletSpawn的朝向向量减去向右偏移rayDetectorMeeleSpace的距离。
    • ray3的起点是bulletSpawn的位置,方向是bulletSpawn的朝向向量。
  2. ray4, ray5, 和 ray6 是上一行的射线:

    • ray4的起点是bulletSpawn的位置向右偏移offsetStart的距离,再向上偏移offsetStart的距离,方向是bulletSpawn的朝向向量加上向右偏移rayDetectorMeeleSpace的距离,再加上向上偏移rayDetectorMeeleSpace的距离。
    • ray5的起点是bulletSpawn的位置向左偏移offsetStart的距离,再向上偏移offsetStart的距离,方向是bulletSpawn的朝向向量减去向右偏移rayDetectorMeeleSpace的距离,再加上向上偏移rayDetectorMeeleSpace的距离。
    • ray6的起点是bulletSpawn的位置向上偏移offsetStart的距离,方向是bulletSpawn的朝向向量加上向上偏移rayDetectorMeeleSpace的距离。
  3. ray7, ray8, 和 ray9 是下一行的射线:

    • ray7的起点是bulletSpawn的位置向右偏移offsetStart的距离,再向下偏移offsetStart的距离,方向是bulletSpawn的朝向向量加上向右偏移rayDetectorMeeleSpace的距离,再减去向下偏移rayDetectorMeeleSpace的距离。
    • ray8的起点是bulletSpawn的位置向左偏移offsetStart的距离,再向下偏移offsetStart的距离,方向是bulletSpawn的朝向向量减去向右偏移rayDetectorMeeleSpace的距离,再减去向下偏移rayDetectorMeeleSpace的距离。
    • ray9的起点是bulletSpawn的位置向下偏移offsetStart的距离,方向是bulletSpawn的朝向向量减去向下偏移rayDetectorMeeleSpace的距离。

这些射线的目的是在多个方向上检测近战攻击是否命中了敌人或障碍物。通过这种方式,游戏可以判断角色的近战攻击是否成功,并相应地触发效果。 

 最后,代码检查当前武器是否具有近战攻击功能。如果当前武器没有进行近战攻击,将been_to_meele_anim设置为false。如果当前武器正在进行近战攻击且尚未进行过近战动画,将been_to_meele_anim设置为true,并启动"MeeleAttackWeaponHit"的协程,用于处理近战攻击的效果。这个协程我们下面会提到。

	IEnumerator MeeleAttackWeaponHit(){
		if (Physics.Raycast (ray1, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray2, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray3, out hitInfo, 2f, ~ignoreLayer)
			|| Physics.Raycast (ray4, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray5, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray6, out hitInfo, 2f, ~ignoreLayer)
			|| Physics.Raycast (ray7, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray8, out hitInfo, 2f, ~ignoreLayer) || Physics.Raycast (ray9, out hitInfo, 2f, ~ignoreLayer)) {
            //在9个方向上进行射线检测
            //Debug.DrawRay (bulletSpawn.position, bulletSpawn.forward + (bulletSpawn.right*0.2f), Color.green, 0.0f);
            if (hitInfo.transform.tag=="Dummie")/*标签*/ {
				Transform _other = hitInfo.transform.root.transform;//获取碰撞对象的根对象(root)的Transform组件
                if (_other.transform.tag == "Dummie") {
					print ("hit a dummie");
				}
				InstantiateBlood(hitInfo,false);
			}
		}
		yield return new WaitForEndOfFrame ();
	}

这段代码是一个名为MeeleAttackWeaponHit的协程函数,主要功能是检测玩家近战攻击是否击中了敌人(标签为"Dummie")。具体分析如下:

1.首先使用Physics.Raycast方法在多个方向上进行射线检测,这些方向由ray1到ray9表示。射线的长度为2f,忽略ignoreLayer指定的层级。

2.如果射线检测到碰撞,将结果存储在hitInfo变量中。

3.检查碰撞对象的标签是否为"Dummie",如果是,则执行以下操作:

    a. 获取碰撞对象的根对象(root)的Transform组件。

    b. 再次检查根对象的标签是否为"Dummie",如果是,则打印"hit a dummie"。

    c. 调用InstantiateBlood方法,传入hitInfo和false作为参数,用于生成血液效果。(这个方法在下面)

4.最后,使用yield return new WaitForEndOfFrame()等待一帧结束,以便在下一帧继续执行。

[Header("BloodForMelleAttaacks")]
RaycastHit hit;//存储命中信息
[Tooltip("Put your particle blood effect here.")]
public GameObject bloodEffect;//血液预制件
/*
* 击中敌人后,它会调用这种方法,给它光线投射命中信息,
* 并在该位置创建我们的血液预制件。
*/
void InstantiateBlood (RaycastHit _hitPos,bool swordHitWithGunOrNot) {		

	if (currentWeapo == "gun") {
		GunScript.HitMarkerSound ();

		if (_hitSound)
			_hitSound.Play ();
		else
			print ("Missing hit sound");
		
		if (!swordHitWithGunOrNot) /*如果没有用剑(近战)击中敌人*/ {
			if (bloodEffect)
				Instantiate (bloodEffect, _hitPos.point, Quaternion.identity);
			else
				print ("Missing blood effect prefab in the inspector.");
		}
	} 
}
private GameObject myBloodEffect;

这段代码用于处理角色近战攻击时敌人被击中后的效果。具体来说,它包含以下几个部分:

  1. 定义了一个RaycastHit类型的变量hit,用于存储射线投射命中的信息。
  2. 定义了一个GameObject类型的公共变量bloodEffect,用于存储血液效果预制件。这个预制件在游戏中会被实例化,以显示敌人被击中时的血液效果。
  3. 定义了一个名为InstantiateBlood的方法,该方法接收两个参数:一个RaycastHit类型的变量_hitPos和一个布尔类型的变量swordHitWithGunOrNot这个方法的作用是在敌人被击中的位置实例化血液效果预制件。
  4. InstantiateBlood方法内部,首先检查当前武器是否为"gun"(枪)。如果是,那么调用GunScript.HitMarkerSound()方法播放击中标记的声音。(这个方法在另一个脚本GunScript里,我这里没写
  5. 如果存在击中声音(_hitSound),则播放该声音;否则,打印一条消息提示缺少击中声音。
  6. 如果swordHitWithGunOrNotfalse(即没有用剑(近战)击中敌人),并且存在血液效果预制件(bloodEffect),则在_hitPos.point位置实例化血液效果预制件。如果没有血液效果预制件,将打印一条消息提示缺少预制件。
[Header("Player SOUNDS")]
[Tooltip("Jump sound when player jumps.")]
public AudioSource _jumpSound;
[Tooltip("Sound while player makes when successfully reloads weapon.")]
public AudioSource _freakingZombiesSound;
[Tooltip("Sound Bullet makes when hits target.")]
public AudioSource _hitSound;
[Tooltip("Walk sound player makes.")]
public AudioSource _walkSound;
[Tooltip("Run Sound player makes.")]
public AudioSource _runSound;

 这部分是通过AudioSource组件实现声音效果,如下:

  1. _jumpSound:当玩家跳跃时播放的声音。
  2. _freakingZombiesSound:当玩家成功重新装填武器时播放的声音。
  3. _hitSound:当子弹击中目标时播放的声音。
  4. _walkSound:当玩家行走时播放的声音。
  5. _runSound:当玩家奔跑时播放的声音。

以上就是关于unity人物的移动脚本的第一部分内容笔记,还有枪械部分等还没写,打算过两天写,我是新手,这是我的第一篇文章,可能会再有的地方写的不完整或是有错误,如果有大佬发现了错误,非常高兴能够指正!

文章参考的知识点链接在这里再放一遍:

Unity3D入门(九):刚体常用方法介绍_fixed timestep参数-CSDN博客

Unity 基础 之 Layer(层layer) 、LayerMask (遮罩层) 的 总结 和 使用(CullingMask、Ray 射线的使用等)_unity layermask-CSDN博客 Unity ref 和out、 params的使用-CSDN博客

Physics.Raycast-CSDN博客

Unity 之 RaycastHit(存储射线投射操作)_unity raycasthit-CSDN博客

  • 20
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值