在之前的文章Unity 3D Input System的使用-CSDN博客中,我介绍了如何用Input System来实现一个FPS游戏的移动控制,这里将进一步完善这个游戏。
以下是游戏的演示效果:
fps_demo
首先是增加主角玩家的武器,我们可以在网上搜索到很多免费的3D资源,例如在以下网址可以下载到一个很不错的3D模型,里面包括了相关的动画。Animated Assault Rifle - Download Free 3D model by JUST (@teenjust500) [46a12c6] (sketchfab.com)
点击下载为FBX格式,并保存到本地。然后打开我们之前的Unity项目,新建一个models文件夹,把下载的3D模型的FBX文件以及相应的材质文件都放到这个目录下。 把这个模型拖动到Player的PlayerCameraRoot下,可以设置模型的scale以调整适合我们的场景。这里我设置的缩放比例是0.08。然后调整一下这个模型的位置,使得能在摄像机里面恰当的显示。点击Play按钮可以看看效果。
这个模型包括了一些动画场景,其中的Idle,Shoot和Reload是FPS中需要用到的。点击展开models里面的HandWithGloves模型,把其中的一个动画,例如Armature|Reload拖动到刚才创建的Game Object,这样就会自动创建Animator,把idle, reload和shoot这三个动画拖动到Animator,然后创建transition,如下图:

在Animator的Parameters里面,新增两个Trigger类型的参数,分别为Reload和Shoot,再增加一个IsReload的Bool类型参数。然后点击从Any State切换到Reload State的线段,在Condition里面选择Reload。点击从Any State切换到Shoot State的线段,在Condition里面选择Shoot以及IsReload为false。
点击Reload这个State,然后添加一个Behavior Script,命名为ReloadControl,其代码如下:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class ReloadControl : StateMachineBehaviour
- {
- // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
- override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- {
- animator.SetBool("IsReload", true);
- }
-
- // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
- //override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- //{
- //
- //}
-
- // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
- override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- {
- animator.SetBool("IsReload", false);
- }
-
- // OnStateMove is called right after Animator.OnAnimatorMove()
- //override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- //{
- // // Implement code that processes and affects root motion
- //}
-
- // OnStateIK is called right after Animator.OnAnimatorIK()
- //override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- //{
- // // Implement code that sets up animation IK (inverse kinematics)
- //}
- }
在项目的Assets->Input目录下,点击之前创建的PlayerInputAsset,增加一个Reload Action,Type是Button,然后添加一个Binding绑定到键盘的R键。增加一个Shoot Action,Type也是Button,Interactions里面增加一个Press,Trigger Behavior设置为Press Only,然后绑定到鼠标的左键。增加一个Shoot Release Action,Type也是Button,Interactions里面增加一个Press,Trigger Behavior设置为Release Only,也是绑定到鼠标的左键。
打开PlayerInputAsset这个脚本文件,在里面增加以下代码:
- public bool reload;
- public bool shoot;
-
- public long shootPressTS = 0;
- public bool firstShoot;
-
- public void OnShoot(InputValue value) {
- shoot = true;
- if (shootPressTS==0) {
- firstShoot = true;
- } else {
- firstShoot = false;
- }
- shootPressTS = DateTime.UtcNow.Ticks;
- }
-
- public void OnShootRelease(InputValue value) {
- shoot = false;
- shootPressTS = 0;
- }
-
- public void OnReload(InputVa