1、导入素材

1.1 素材

1.2 创建游戏对象

新建3d游戏对象Plane,作为地面

挂载贴图,调整地面尺寸

2、实现人物移动

2.1 创建胶囊体游戏对象

 自带胶囊碰撞体


为此对象创建脚本,添加角色控制器组件并去掉自带胶囊碰撞体组件

2.2 写移动脚本

移动物体的方法有两种1、CharacterController组件实现2、RigidBody刚体实现

    public float moveSpeed = 10f;//移动速度public Vector3 moveDirection;//三维向量,表示移动方向public void PlayerMove(){float h_Value= Input.GetAxis("Horizontal");float v_Value = Input.GetAxis("Vertical");moveDirection = (transform.right * h_Value + transform.forward * v_Value).normalized;//红色方向轴和蓝色方向轴,形成三维方向向量,并标准化characterController.Move(moveDirection*moveSpeed*Time.deltaTime);}

3、实现镜头跟随

3.1 使主摄像机成为Player的子物体

调整坐标位置使摄像机刚好在Player头上,控制Player就可以控制相机

3.2 创建脚本控制相机旋转

将脚本挂载到相机上,使用InputManager的鼠标控制X轴和Y轴

public float mouseSensitivity = 100f;//视角灵敏度public Transform playerPos;//玩家位置public float xRotation = 0f;//存放累加值// Start is called before the first frame updatevoid Start(){//隐藏光标,避免画面中出现鼠标Cursor.lockState = CursorLockMode.Locked;}// Update is called once per framevoid Update(){//将轴映射到鼠标,值为当前鼠标增量乘以轴灵敏度float mouseX= Input.GetAxis("Mouse X")*mouseSensitivity*Time.deltaTime;float mouseY= Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;//坐标跟随鼠标移动,上下是Y,左右是Xprint("Mouse X:" + mouseX);print("Mouse Y:" + mouseY);//玩家左右旋转视角playerPos.Rotate(Vector3.up* mouseX);//(0,1,0),物体绕Y轴横向旋转//相机旋转xRotation -= mouseY;//累加绕X轴上下旋转的轴值xRotation = Mathf.Clamp(xRotation, -80f, 80f);//限制旋转角度(轴值累积)transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);//相机绕X轴上下旋转改变角度}

4、实现人物奔跑

可以动态改变速度,分别声明行走速度和奔跑速度,获取键盘(KeyCode)输入

ps:声明变量前加上[Tooltip("xxx")],即可在面板对应变量悬停显示提示信息;[SerializeField]强制序列化,使私有变量显示在面板;[Header("xxx")]给变量加上标题,方便后续区分变量用途

    public float moveSpeed = 10f;//移动速度public float walkSpeed = 20f;//按键区分速度public float runSpeed = 15f;public bool isRun;//奔跑判断[Header("按键设置")]public KeyCode runKeyInput;//奔跑键,左shiftpublic void PlayerMove(){isRun = Input.GetKey(runKeyInput);if(isRun){moveSpeed = runSpeed;}else{moveSpeed = walkSpeed;}}

5、实现人物跳跃

Y轴坐标矢量变换,需要向上的力和向下的重力

5.1 向上

     public bool isJump;public float jumpForce = 3f;//跳跃力度public Vector3 velocity;//力,Y轴的一个冲量变化public KeyCode jumpKeyInput;//跳跃键,空格public void PlayerMove(){PlayerJump();characterController.Move(velocity * Time.deltaTime);}public void PlayerJump(){isJump = Input.GetKey(jumpKeyInput);if(isJump){velocity.y = jumpForce * 2f;}}

5.2 增加地面检查点

  1. 当物体与地面碰撞时,说明物体已经落地,向下的重力消失
  2. 新建Player的子物体checkGround
  3. 新建地面检测方法在移动前调用并每帧执行
 void Update(){GroundCheck();PlayerMove();}
  1. 为地面设置Layer层级
    public bool isGround;//地面碰撞判断private Transform groundCheck;//地面检测点,初始化CheckGroundprivate float groundDistance = 0.1f;//与地面的距离,私有变量public LayerMask groundMesh;//地面层级,使小球与地面一个层,所以物理小球只与地面碰撞public void GroundCheck(){//生成物理球体与地面碰撞,球体位置,半径,层级,层级与地面相同//碰撞点是CheckGround坐标,调整其位置到接近地面处isGround= Physics.CheckSphere(groundCheck.position,groundDistance,groundMesh);//Debug.Log(isGround);//在地面时给一个向下的力if(isGround &&velocity.y<=0){velocity.y = -2f;}}

5.3 起跳后向下

    public float jumpForce = 3f;//跳跃力度public float gravity = -20f;//向下的重力public Vector3 velocity;//力,Y轴的一个冲量变化/// <summary>/// 移动/// </summary>public void PlayerMove(){if(isGround==false)//不接地,在空中累加向下的力{velocity.y += gravity * Time.deltaTime;}characterController.Move(velocity * Time.deltaTime);PlayerJump();}/// <summary>/// 跳跃/// </summary>public void PlayerJump(){isJump = Input.GetKey(jumpKeyInput);if(isJump&&isGround)//按下空格并且在地上{velocity.y =Mathf.Sqrt ( jumpForce * -2f*gravity);}}

6、人物上坡

cube搭建一个斜坡
运行发现人物无法在斜坡上跳跃:修改groundCheck坐标位置,或坐标到地面的半径大小

Player在斜坡上行走不平稳,解决:检测Player在斜坡上时,额外施加一个向下的力

public bool OnSlpe(){if(isJump)return false;RaycastHit hit;//存放反射出的射线//向下打出射线//(Player)向场景中所有碰撞体投射一条射线,反射射线不垂直向上(0,1,0),则Player在斜边if(Physics.Raycast(transform.position,Vector3.down,out hit,characterController.height/2*slopForcrRayLength)){ if(hit.normal!=Vector3.up)//normal是法线,方向与(0,1,0)不一致{return true;}}return false;}
 if(OnSlpe())//如果在斜坡{characterController.Move(Vector3.down * characterController.height / 2 * slopForce * Time.deltaTime);}

7、实现射击

7.1 找到素材中枪械预制体

 预制体挂载到主相机下,采用射线检测实现射击的发生


枪械位置归零


修改摄像机渲染距离(Clipping Planes),解决枪视角穿模
添加UI,image作为准心

    public Transform shootPoint;//武器射击位置(子弹出膛位置public int range = 100;//子弹射程//右键瞄准,左键射击private bool gunShootInput;  void Update(){gunShootInput = Input.GetMouseButton(0);//左键按下if(gunShootInput){GunFire();}}/// <summary>/// 射击/// </summary>public void GunFire(){//向前射击Vector3 shootDirection = shootPoint.forward;RaycastHit hit;//发出射线if(Physics.Raycast(shootPoint.position,shootDirection,out hit,range))//击中目标信息存入hit{Debug.Log(hit.transform.name+"打到惹");}}

发出的射线击中目标物体(但是可以对Player对象进行射击)
在Update函数一秒60帧,需要自己手动控制射速

7.2 控制射速

    //射速控制,计时器public float fireRate = 0.1f;public float fireTimer;void Update(){if(fireTimer<fireRate)//射速越小,打出的射线越少{fireTimer += Time.deltaTime;}}public void GunFire(){//计时器值比射速还小if (fireTimer < fireRate) return;//直接返回跳出函数//向前射击//发出射线fireTimer = 0f;//重置计时器}
}

7.2 实现打空弹匣

    public int bulletsMag = 30;//一个弹匣子弹数量public int range = 100;//子弹射程public int bulletsLeft = 300;//剩余备用子弹public int currentBullets;//当前子弹数void Start(){currentBullets = bulletsMag;}public void GunFire(){//计时器值比射速还小if (fireTimer < fireRate||currentBullets<=0) return;//直接返回跳出函数//向前射击//发出射线currentBullets--;//子弹减少}

7.3 子弹数UI

 添加文本(Text)UI再脚本设置UI

    [Header("UI设置")]public Image crossHairUI;//初始化public Text AmmoTextUI;void Start(){currentBullets = bulletsMag;UpdateAmmoUI();//初始化一下}/// <summary>/// 射击/// </summary>public void GunFire(){currentBullets--;//子弹减少//每次子弹减少,就调用UIUpdateAmmoUI();fireTimer = 0f;//重置计时器}    public void UpdateAmmoUI(){AmmoTextUI.text = currentBullets + "/" + bulletsLeft;}

7.4 实现换弹装弹

R键换弹
    [Header("键位设置")][Tooltip("装填子弹的按键")][SerializeField] private KeyCode relodInputKey;//换弹键public void Relod(){if (bulletsLeft <= 0) return;//余弹不足//计算需要装填的子弹数量=弹匣子弹总数-当前子弹数int bulletRelod = bulletsMag - currentBullets;//备弹需要扣除子弹数int bulletReduce = (bulletsLeft > bulletRelod) ?  bulletRelod: bulletsLeft;bulletsLeft -= bulletReduce;//备弹减少currentBullets += bulletReduce;//弹匣加装填弹UpdateAmmoUI();}

8、添加音效

8.1 添加音源

在Player身上添加音源组件

8.2 行走和跑步音效


在PlayerMove()进行调用

    /// <summary>/// 播放移动音效/// </summary>public void PlayMoveAudio(){if(isGround&&moveDirection.sqrMagnitude>0.9f)//在地上且在走动{audioSource.clip = isRun ? runningAudio : walkingAudio;//走或跑if(!audioSource.isPlaying){audioSource.Play();//没在播放时播放}}else{if(audioSource.isPlaying){audioSource.Pause();}}}

9、开枪特效

9.1 添加开枪粒子特效

添加粒子特效预制体

脚本控制

    public ParticleSystem muzzleFlash;//粒子特效public Light muzzleFlashLight;//灯光//粒子火花特效muzzleFlash.Play();muzzleFlashLight.enabled = true;//启用灯光

9.2 生成弹孔

    public GameObject hitParticle;//击中特效public GameObject bulletHole;//弹孔//发出射线if(Physics.Raycast(shootPoint.position,shootDirection,out hit,range))//击中目标信息存入hit{Debug.Log(hit.transform.name+"打到惹");GameObject hitParticleEffect= Instantiate(hitParticle, hit.point, Quaternion.FromToRotation(Vector3.up, hit.normal));//实例化克隆对象,在被击中位置生成火花特效GameObject bulletHoleEffect= Instantiate(bulletHole, hit.point, Quaternion.FromToRotation(Vector3.up, hit.normal));//实例化克隆对象,在被击中位置生成弹孔Destroy(hitParticleEffect, 1f);Destroy(bulletHoleEffect, 3f);//一定时间后销毁}

10、Player手臂摇晃

public class WeaponSway : MonoBehaviour
{/*摇摆参数*/public float amout;//摆动幅度public float smoothAmout;//摇摆平滑值public float maxAmout;//最大幅度摇摆[SerializeField]private Vector3 originPosition;//起始位置// Start is called before the first frame updatevoid Start(){originPosition = transform.localPosition;//相对于父级物体的变换位置}// Update is called once per framevoid Update(){//获取鼠标轴值float mouseX=  -Input.GetAxis("Mouse X")*amout;float mouseY = -Input.GetAxis("Mouse Y") * amout;//限制mouseX = Mathf.Clamp(mouseX, -maxAmout, maxAmout);mouseY = Mathf.Clamp(mouseY, -maxAmout, maxAmout);//手臂位置变化Vector3 finallyPosition = new Vector3(mouseX, mouseY, 0);//左右摇晃transform.localPosition = Vector3.Lerp(transform.localPosition, finallyPosition + originPosition, Time.deltaTime * smoothAmout);}
}

11、动画

Animator组件:动画状态机,管理动画
项目中新建Animation文件夹并新建Animator Controller组件管理动画
挂载到对应武器的状态机上

11.1 拿出武器、静止、查看武器

1、Exit:全部执行后退出
2、Any Status:任何状态
3、Entry:动画块入口
1. 添加拿出武器动作

2. 添加站立待机动画,对take_out右键make transition指向待机动画块

3. 添加查看武器动画,动画能从待机动画传递到检视武器动画,并且能传递回去设置进入动画条件:F键检视武器添加触发器类型参数




触发器设为idle->inspect_weapon的触发条件

    [Tooltip("查看武器的按键")] [SerializeField] private KeyCode inspectWeaponKey;//检查武器动画void Start(){anim = GetComponent<Animator>();//获取组件}void Update(){//检查武器if(Input.GetKeyDown(inspectWeaponKey)){anim.SetTrigger("Inspect");//触发器获取参数}}

11.2 行走和奔跑动画

在PlayerMovement脚本
设置动画的触发条件
public bool isWalk;//动画触发条件
void Update(){//处于行走状态isWalk = (Mathf.Abs(h_Value) > 0 || Mathf.Abs(v_Value) > 0) ? true : false;//不大于零就说明没在走
}
在WeaponController脚本调用PlayerMovement中的变量public PlayerMovement PM;//调用PlayerMovement中变量anim.SetBool("Run", PM.isRun);//键,值anim.SetBool("Walk", PM.isWalk);

在Animator添加对应bool参数Run/Walk


11.3 换弹动画

  1. 弹匣打空换弹
  2. 弹匣未打空换弹
    /// <summary>/// 播放换弹动画/// </summary>public void RelodAnimation(){//播放动画1if(currentBullets>0){anim.Play("reload_ammo_left", 0, 0);//0表示Base LayeraudioSource.clip=relodAmmoLeftClip;audioSource.Play();}if(currentBullets==0&&bulletsMag>0){anim.Play("reload_out_of_ammo", 0, 0);audioSource.clip = relodOutOfAmmoClip;audioSource.Play();}}


anim.Play()直接播放动画,就不需要画箭头传递,只需要动画播放完毕传回待机状态

处理奔跑、换弹时可以射击的问题
  private bool isRelod;//判断是否装弹AnimatorStateInfo info = anim.GetCurrentAnimatorStateInfo(0);//获取当前第一层动画信息
void Update(){        if(info.IsName("reload_ammo_left")||info.IsName("reload_out_of_ammo"))//获取两个动画名称{isRelod = true;//正在换弹}else{isRelod = false;}}public void GunFire(){//计时器值比射速还小if (fireTimer < fireRate||currentBullets<=0||isRelod||PM.isRun) return;//直接返回跳出函数   }

最终结果:demo文件

基于u3d_FPS_Demo相关推荐

  1. 基于Golang的简单web服务程序开发——CloudGo

    基于Golang的简单web服务程序开发--CloudGo[阅读时间:约10分钟] 一.概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 (1)基本要求 (2)扩展要求 三.具体 ...

  2. 【ReactiveX】基于Golang pmlpml/RxGo程序包的二次开发

    基于Golang pmlpml/RxGo程序包的二次开发[阅读时间:约20分钟] 一.ReactiveX & RxGo介绍 1.ReactiveX 2.RxGo 二.系统环境&项目介绍 ...

  3. 基于Golang的对象序列化的程序包开发——myJsonMarshal

    基于Golang的对象序列化的程序包开发--myJsonMarshal[阅读时间:约10分钟] 一.对象序列化概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 三.具体程序设计及 ...

  4. 基于Golang的监听读取配置文件的程序包开发——simpleConfig_v1

    基于Golang的监听&读取配置文件的程序包开发--simpleConfig_v1 [阅读时间:约10分钟] 一.配置文件概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 ...

  5. 基于Golang的CLI 命令行程序开发

    基于Golang的CLI 命令行程序开发 [阅读时间:约15分钟] 一. CLI 命令行程序概述 二. 系统环境&项目介绍&开发准备 1.系统环境 2.项目介绍 3.开发准备 三.具体 ...

  6. etcd 笔记(08)— 基于 etcd 实现分布式锁

    1. 为什么需要分布式锁? 在分布式环境下,数据一致性问题一直是个难点.分布式与单机环境最大的不同在于它不是多线程而是多进程.由于多线程可以共享堆内存,因此可以简单地采取内存作为标记存储位置.而多进程 ...

  7. OpenCV 笔记(08)— 二维点、三维点、基于 Mat 的 std::vector 等常用数据结构的定义和输出

    1. 定义和输出二维点 Point2f p2(3, 4);cout << "[二维点] is "<< endl << p2 << e ...

  8. python中的新式类与旧式类的一些基于descriptor的概念(下)

    3. Descriptor介绍 3.1 Descriptor代码示例 3.2 定义 3.3 Descriptor Protocol(协议) 3.4 Descriptor调用方法 4. 基于Descri ...

  9. python中的新式类与旧式类的一些基于descriptor的概念(上)

    python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...

最新文章

  1. 初学 Java Web 开发,请远离各种框架,从 Servlet 开发
  2. 体积小巧、功能强大的代理工具 -- 3proxy
  3. linux命令行怎么注释,Bash Shell 注释多行的几种方法
  4. python中选择结构通过什么语句实现_Python中选择结构通过什么语句实现
  5. Shell脚本中$的用法
  6. 前端学习(2887):如何短时间内实现v-for createApp解决方案
  7. 防止arp***方法
  8. 2021年国货彩妆品牌推广营销趋势
  9. mysql完全卸载大全
  10. 正则表达式:re.match、re.search、re.sub、re.compile、findall、re.finditer、re.split
  11. dup和dup2(摘 )
  12. 【Python】python list 迭代删除
  13. wifi技术扫盲-MIMO
  14. 奥城大学计算机专业,美国研究生双录取的大学及可提供学位详情
  15. dtu虚拟服务器,DTU服务器云
  16. background 渐变背景
  17. 越狱软件可带来千万量级用户
  18. 全国计算机四级薪资,全国计算机四级通过率有多少
  19. 安装minidwep-gtk出现了“离开目录”的错误
  20. 阿里云国际版ECS云服务器ping不通的原因分析

热门文章

  1. 让你的站点(Web)一键变成APP(应用程序)(上)
  2. -XX:+UseParallelGC与 -XX:+UseParNewGC 区别
  3. Ubuntu下图片转pdf和pdf合并
  4. 用STM32CubeMX生成STM32F407ZG + LAN8720A 的LWIP
  5. 正式版上线、登录币安NFT市场,PlatoFarm近况
  6. audio音频不能自动播放的解决方法
  7. Mega软件操作教程
  8. eNSP 路由器配置-静态路由和缺省路由
  9. 电镀行业水处理分析:褪镀废水回收重金属,用什么工艺解决
  10. 2015美国大学计算机科学专业排名,2015美国大学本科计算机专业排名前一百