【unity实战】一个通用的FPS枪支不同武器射击控制脚本

文章目录

  • 前言
  • 模型素材
  • 文章用到的粒子火光特效
  • 射击效果
  • 换弹
  • 瞄准
  • 开枪抖动效果
  • 设置显示文本
  • 最终代码
  • 不同武器射击效果
    • 1. 手枪
    • 2. 机枪
    • 3. 狙击枪
    • 4. 霰弹枪
    • 5. 加特林
  • 其他
  • 感谢
  • 完结

前言

实现FPS枪支不同武器效果,比如手枪,喷子,狙击枪,机枪,其实我最开始的想法是先做一个基类脚本,写一些公共属性和方法,然后再起不同的武器脚本这个基础基类,实现不同的武器效果。

这样的实现思路其实是没什么问题的,直到我看到这个视频:https://www.youtube.com/watch?v=bqNW08Tac0Y,作者只用一个脚本就实现了不同的武器效果更加方便,下面我就参考一下作者的思路实现一下大致的效果。

顺带说一下,在第一人称射击(FPS)游戏中实现子弹射击效果,可以通过不同的技术和方法来完成。以下是几种常见的实现方式:

  1. 射线投射(Raycasting):
    这是最常用的方法之一。射线投射意味着从枪口发出一个虚拟的射线,并检测这个射线与游戏世界中的对象之间的交互。如果射线与某个对象相交,那么就可以认为子弹击中了该对象。

    实现步骤:

    • 从玩家的摄像机或枪口位置发出一条射线。
    • 使用物理引擎提供的射线投射功能来检测射线路径上的碰撞。
    • 如果射线与对象相交,根据交互结果执行相应的逻辑,比如扣除生命值、播放受击动画等。
    • 在射击点显示击中效果,如粒子效果或贴图。
  2. 抛射物模拟(Projectile Simulation):
    对于需要模拟子弹飞行轨迹的情况,比如远距离狙击、火箭筒或者抛射武器,可以使用抛射物模拟。

    实现步骤:

    • 创建一个子弹实体,并赋予它初始速度和方向。
    • 通过物理引擎模拟子弹的飞行轨迹,考虑重力、空气阻力等因素。
    • 检测子弹与其他对象的碰撞,并在碰撞发生时处理相应的逻辑。
    • 在子弹飞行过程中可以添加轨迹效果,如拖尾。

每种方法都有其适用场景和优缺点。射线投射适合快速射击和近距离交火,抛射物模拟适合远距离和弧线射击。在实际开发中,这些方法可以组合使用,以达到最佳的效果。

模型素材

不会配置模型可以看我之前的文章,进行下载和配置:
unity中导入下载的3D模型及albedo/baseColor、normal 、AO/Occlus、metallic、roughness贴图纹理设置

文章用到的粒子火光特效

https://assetstore.unity.com/packages/vfx/particles/legacy-particle-pack-73777
在这里插入图片描述

射击效果

[Tooltip("是否正在射击")]
bool shooting;
[Tooltip("是否允许按住射击")]
public bool allowButtonHold;
[Tooltip("是否可以射击")]
bool readyToShoot;
[Tooltip("是否在换弹")]
bool reloading;
[Tooltip("弹夹容量")]
public int magazineSize;
[Tooltip("当前弹夹容量")]
public int bulletsLeft;
[Tooltip("储备弹药容量")]
public int reservedAmmoCapacity = 300;
[Tooltip("当前剩余射击发射的子弹数")]
public int bulletsShot;
[Tooltip("枪口火焰特效")]
public ParticleSystem muzzleFlash;
[Tooltip("子弹击中效果")]
public GameObject bulletHoleGraphic;
[Tooltip("射击间隔时间")]
public float timeBetweenShooting;
[Tooltip("连发射击之间的间隔时间")]
public float timeBetweenShots;
[Tooltip("射击时的散布度")]
public float spread;
[Tooltip("射击的最大距离")]
public float range;
[Tooltip("每次射击发射的子弹数")]
public int bulletsPerTap;
[Tooltip("是否允许按住射击")]
public bool allowButtonHold;
[Tooltip("每次射击造成的伤害")]
public int damage;  // 伤害public Camera fpsCam;private void Awake()
{bulletsLeft = magazineSize;readyToShoot = true;
}private void Update()
{MyInput();
}private void MyInput()
{if (allowButtonHold)shooting = Input.GetKey(KeyCode.Mouse0);elseshooting = Input.GetKeyDown(KeyCode.Mouse0);// 射击if (readyToShoot && shooting && !reloading && bulletsLeft > 0){bulletsShot = bulletsPerTap;Shoot();}
}private void Shoot()
{readyToShoot = false;// 散布float x = Random.Range(-spread, spread);float y = Random.Range(-spread, spread);// 计算带有散布的射击方向Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);//场景显示红线,方便调试查看Debug.DrawRay(fpsCam.transform.position, direction * range, Color.red);// 射线检测if (Physics.Raycast(fpsCam.transform.position, direction, out RaycastHit rayHit, range)){Debug.Log(rayHit.collider.name);muzzleFlash.Play();//枪口火焰/火光//TODO:相机震动if (rayHit.collider.CompareTag("Enemy")){Debug.Log("击中敌人");Rigidbody rb = rayHit.transform.GetComponent<Rigidbody>();if (rb != null){rb.constraints = RigidbodyConstraints.None; // 解除刚体约束rb.AddForce(transform.parent.transform.forward * 500); // 给敌人施加一个力}// 击中敌人特效var res1 = Instantiate(bulletHoleGraphic, rayHit.point, Quaternion.Euler(0, 180, 0));Destroy(res1, 0.5f);//TODO:扣血}}bulletsLeft--;bulletsShot--;Invoke("ResetShot", timeBetweenShooting);if (bulletsShot > 0 && bulletsLeft > 0)Invoke("Shoot", timeBetweenShots);
}private void ResetShot()
{readyToShoot = true;
}

换弹

private void MyInput()
{//。。。if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading)Reload();
}//换弹
private void Reload()
{reloading = true;Invoke("ReloadFinished", reloadTime);
}private void ReloadFinished()
{if (reservedAmmoCapacity <= 0) return;//计算需要填装的子弹数=1个弹匣子弹数-当前弹匣子弹数int bullectToLoad = magazineSize - bulletsLeft;//计算备弹需扣除子弹数int bullectToReduce = (reservedAmmoCapacity >= bullectToLoad) ? bullectToLoad : reservedAmmoCapacity;reservedAmmoCapacity -= bullectToReduce;//减少备弹数bulletsLeft += bullectToReduce;//当前子弹数增加bulletsLeft = magazineSize;reloading = false;
}

瞄准

private void MyInput()
{//。。。//瞄准DetermineAim();
}void DetermineAim()
{Vector3 target = normalLocalPosition; // 默认目标位置为正常瞄准时的本地位置if (Input.GetMouseButton(1)) target = aimingLocalPosition; // 如果按下鼠标右键,目标位置为瞄准时的本地位置Vector3 desiredPosition = Vector3.Lerp(transform.localPosition, target, Time.deltaTime * aimSmoothing); // 使用插值平滑过渡到目标位置transform.localPosition = desiredPosition; // 更新枪支的本地位置
}

效果
在这里插入图片描述

开枪抖动效果

如果你的枪模型没有开枪动画的话,这个方法就很方便了

private void Shoot()
{transform.localPosition -= Vector3.forward * 0.1f; // 后坐力使枪支向后移动//。。。
}

设置显示文本

private void Update()
{//。。。SetUI();
}// 设置文本
private void SetUI()
{text.SetText(bulletsLeft + " / " + reservedAmmoCapacity);
}

最终代码

public class GunSystem : MonoBehaviour
{public Camera fpsCam;[Header("枪械状态")][Tooltip("是否正在射击")]bool shooting;[Tooltip("是否可以射击")]bool readyToShoot;[Tooltip("是否在换弹")]bool reloading;[Header("弹夹")][Tooltip("弹夹容量")]public int magazineSize;[Tooltip("当前弹夹容量")]public int bulletsLeft;[Tooltip("储备弹药容量")]public int reservedAmmoCapacity = 300;[Tooltip("当前剩余射击发射的子弹数")]public int bulletsShot;[Header("射击")][Tooltip("射击间隔时间")]public float timeBetweenShooting;[Tooltip("射击时的散布度")]public float spread;[Tooltip("射击的最大距离")]public float range;[Tooltip("每次射击发射的子弹数")]public int bulletsPerTap;[Tooltip("是否允许按住射击")]public bool allowButtonHold;[Tooltip("每次射击造成的伤害")]public int damage;  // 伤害[Tooltip("装填弹药的时间")]public float reloadTime;[Tooltip("连发射击之间的间隔时间")]public float timeBetweenShots;[Header("瞄准")][Tooltip("正常情况的本地位置")]public Vector3 normalLocalPosition;[Tooltip("瞄准时的本地位置")]public Vector3 aimingLocalPosition;[Tooltip("瞄准过程的平滑度")]public float aimSmoothing = 10;[Header("效果")][Tooltip("枪口火焰特效")]public ParticleSystem muzzleFlash;[Tooltip("子弹击中效果")]public GameObject bulletHoleGraphic;[Header("UI")]public TextMeshProUGUI text;  // 弹药显示文本private void Awake(){bulletsLeft = magazineSize;readyToShoot = true;}private void Update(){MyInput();SetUI();}// 设置文本private void SetUI(){text.SetText(bulletsLeft + " / " + reservedAmmoCapacity);}private void MyInput(){if (allowButtonHold)shooting = Input.GetKey(KeyCode.Mouse0);elseshooting = Input.GetKeyDown(KeyCode.Mouse0);// 射击if (readyToShoot && shooting && !reloading && bulletsLeft > 0){bulletsShot = bulletsPerTap;Shoot();}//换弹if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading)Reload();//瞄准DetermineAim();}private void Shoot(){readyToShoot = false;transform.localPosition -= Vector3.forward * 0.1f; // 后坐力使枪支向后移动// 散布float x = Random.Range(-spread, spread);float y = Random.Range(-spread, spread);// 计算带有散布的射击方向Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);//场景显示红线,方便调试查看Debug.DrawRay(fpsCam.transform.position, direction * range, Color.red);// 射线检测if (Physics.Raycast(fpsCam.transform.position, direction, out RaycastHit rayHit, range)){Debug.Log(rayHit.collider.name);muzzleFlash.Play();//枪口火焰/火光//相机震动if (rayHit.collider.CompareTag("Enemy")){Debug.Log("击中敌人");// Rigidbody rb = rayHit.transform.GetComponent<Rigidbody>();// if (rb != null)// {//     rb.constraints = RigidbodyConstraints.None; // 解除刚体约束//     rb.AddForce(transform.parent.transform.forward * 500); // 给敌人施加一个力// }// 击中敌人特效var res = Instantiate(bulletHoleGraphic, rayHit.point, Quaternion.Euler(0, 180, 0));res.transform.parent = rayHit.transform;//设置父类//TODO:扣血}}bulletsLeft--;bulletsShot--;Invoke("ResetShot", timeBetweenShooting);if (bulletsShot > 0 && bulletsLeft > 0)Invoke("Shoot", timeBetweenShots);}void DetermineAim(){Vector3 target = normalLocalPosition; // 默认目标位置为正常瞄准时的本地位置if (Input.GetMouseButton(1)) target = aimingLocalPosition; // 如果按下鼠标右键,目标位置为瞄准时的本地位置Vector3 desiredPosition = Vector3.Lerp(transform.localPosition, target, Time.deltaTime * aimSmoothing); // 使用插值平滑过渡到目标位置transform.localPosition = desiredPosition; // 更新枪支的本地位置}private void ResetShot(){readyToShoot = true;}//换弹private void Reload(){reloading = true;Invoke("ReloadFinished", reloadTime);}private void ReloadFinished(){if (reservedAmmoCapacity <= 0) return;//计算需要填装的子弹数=1个弹匣子弹数-当前弹匣子弹数int bullectToLoad = magazineSize - bulletsLeft;//计算备弹需扣除子弹数int bullectToReduce = (reservedAmmoCapacity >= bullectToLoad) ? bullectToLoad : reservedAmmoCapacity;reservedAmmoCapacity -= bullectToReduce;//减少备弹数bulletsLeft += bullectToReduce;//当前子弹数增加bulletsLeft = magazineSize;reloading = false;}
}

不同武器射击效果

注意:这里为了方便,我就用一把枪做演示了

1. 手枪

参数配置
在这里插入图片描述
效果
在这里插入图片描述

2. 机枪

参数
在这里插入图片描述
效果
在这里插入图片描述

3. 狙击枪

参数,狙击枪其实和手枪参数差不多,可以就需要修改射击间隔时间、换弹时间和伤害
在这里插入图片描述
效果
在这里插入图片描述

4. 霰弹枪

参数
在这里插入图片描述

效果
在这里插入图片描述

5. 加特林

参数
在这里插入图片描述

效果
在这里插入图片描述

其他

可以看到其实还有很多功能没有实现,比如后座力或者放大镜等等效果,这篇文章说的已经够多了,后面我再单独做其他内容的探究吧!

感谢

【视频】https://www.youtube.com/watch?v=bqNW08Tac0Y

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/212650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《使用ThinkPHP6开发项目》 - 创建应用

《使用ThinkPHP6开发项目》 - 安装ThinkPHP框架-CSDN博客 《使用ThinkPHP6开发项目》 - 设置项目环境变量-CSDN博客 《使用ThinkPHP6开发项目》 - 项目使用多应用开发-CSDN博客 根据前面的步骤&#xff0c;我们现在就可以开发我们的项目开发了&#xff0c;根据项目开发的需要…

【数据挖掘】国科大苏桂平老师数据库新技术课程作业 —— 第四次作业

云数据库研究 云计算与云数据库背景 云计算&#xff08;cloud computing&#xff09;是 IT 技术发展的最新趋势&#xff0c;正受到业界和学术界的广泛关注。云计算是在分布式处理、并行处理和网格计算等技术的基础上发展起来的&#xff0c;是一种新兴的共享基础架构的方法。它…

[足式机器人]Part4 南科大高等机器人控制课 Ch05 Instantaneous Velocity of Moving Frames

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;CLEAR_LAB 笔者带更新-运动学 课程主讲教师&#xff1a; Prof. Wei Zhang 南科大高等机器人控制课 Ch05 Instantaneous Velocity of Moving Frames 1.Instantanenous Velocity of Rotating Frames2.Instantanenous Veloc…

最新Redis7持久化(权威出版)

首先我们要知道什么是持久化&#xff1a;持久化是指将数据保存到磁盘上&#xff0c;以确保在Redis服务器重启时数据不会丢失。 Redis支持两种主要的持久化方式&#xff1a;RDB持久化和AOF持久化 下面让我依次给你介绍一下&#xff1a; RDB持久化 作用 这是将Redis数据保存…

Java语言概述及保姆级入门教程(JDK 17版本)

笔记来自尚硅谷老师-康老师 学习教程&#xff1a;https://www.bilibili.com/video/BV1PY411e7J6/?spm_id_from333.337.search-card.all.click 1、Java基础全程脉络图 1.1 本章专题与脉络 2. 抽丝剥茧话Java 2.1 当前大学生就业形势 麦可思研究院发布了《2022年中国大学生就业…

C#图像处理OpenCV开发指南(CVStar,09)——边缘识别之Scharr算法的实例代码

1 边缘识别之Scharr算法 算法文章很多&#xff0c;不再论述。 1.1 函数原型 void Cv2.Scharr(src,dst,ddepth,dx,dy,scale,delta,borderType&#xff09; 1.2 参数说明 src 代表原始图像。dst 代表目标图像。ddepth 代表输出图像的深度。CV_16Sdx 代表x方向上的求导阶数…

uniApp应用软件在运行时,不符合华为应用市场审核标准。解决方案合集!

&#xff08;暂时用不到的也建议收藏一下&#xff0c;因为文章持续更新中&#xff09; 最新更改时间&#xff1a;20023-12-10 第一次做App应用开发相信大家一定都遇到过华为应用市场审核的“驳回”&#xff01; 有些问题一看就明白可以立马修改&#xff0c;而有一些问题修改意…

Dubbo入门直接上手,结合微服务详解

Dubbo 高性能、轻量级的 Java RPC 框架 RPC&#xff1a; Remote Procedure Call 远程过程调用&#xff0c;简单来说就是它允许一个计算机程序通过网络请求调用另一个计算机上的程序&#xff0c;就像本地调用一样。有非常多的协议和技术来都实现了RPC的过程&#xff0c;比如&a…

Elasticsearch 8.9 refresh刷Es缓冲区的数据到Lucene,更新segemnt,使数据可见

一、相关API的handler1、接受HTTP请求的hander(RestRefreshAction)2、往数据节点发送刷新请求的action(TransportRefreshAction)3、数据节点接收主节点refresh传输的action(TransportShardRefreshAction) 二、在IndexShard执行refresh操作1、根据入参决定是使用lucene提供的阻塞…

【设计模式--创建型--原型模式】

设计模式--创建型--原型模式 原型模式概述结构实现结果 案例代码结果使用场景 扩展&#xff08;深\浅克隆&#xff09;浅克隆演示&#xff1a;结果&#xff1a;使用深克隆&#xff08;利用对象流&#xff09;结果 原型模式 概述 用一个已经创建的实例作为原型&#xff0c;通过…

Spring Cloud Gateway + Nacos + LoadBalancer实现企业级网关

1. Spring Cloud Gateway 整合Nacos、LoadBalancer 实现企业级网关 前置工作&#xff1a; 创建 SpringBoot 多模块项目创建网关&#xff08;gateway-service&#xff09;、用户&#xff08;user-service&#xff09;模块用户模块添加 Nacos discovery 支持以及 Spring Web&am…

gitbash下载安装

参考教程 零、下载 官网地址 2.43.0win64 链接&#xff1a;https://pan.baidu.com/s/16urs_nmky7j20-qNzUTTkg 提取码&#xff1a;7jaq 一、安装 图标组件&#xff08;Additional icons&#xff09;&#xff1a;选择是否创建桌面快捷方式&#xff1b;桌面浏览&#xff08;Win…

逸迅科技丁红阳:三种能力帮助企业打造GBI “护城河”

大数据产业创新服务媒体 ——聚焦数据 改变商业 近日&#xff0c;由上海市经济和信息化委员会、上海市科学技术委员会指导&#xff0c;数据猿与上海大数据联盟联合主办的“2023企业数智化转型升级发展论坛”在上海举行。本次论坛以“释放数字价值驱动智能升级”为主题&#xf…

piakachu越权漏洞

水平越权 首先打开这一关&#xff0c;在右侧有一些提示&#xff0c;我们可以看到 然后我们随便输入一组信息即可&#xff0c;可以在url中看到这样的字段 当我们尝试在url中直接更换另一个用户名时可以发现&#xff0c;直接切换到了另一个用户的身份 垂直越权 这里可以看到右边…

一文学会使用 PyInstaller 将 Python 脚本打包为 .exe 可执行文件

文章目录 前言PyInstaller特点跨平台支持自动依赖项处理单文件发布支持图形用户界面&#xff08;GUI&#xff09;和命令行界面&#xff08;CLI&#xff09;应用支持多种打包选项 基本用法常用参数其它参数 版本 & 环境实现步骤安装 PyInstaller创建 Python 脚本使用 PyInst…

【SpringBoot教程】SpringBoot 创建定时任务(配合数据库动态执行)

作者简介&#xff1a;大家好&#xff0c;我是撸代码的羊驼&#xff0c;前阿里巴巴架构师&#xff0c;现某互联网公司CTO 联系v&#xff1a;sulny_ann&#xff08;17362204968&#xff09;&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗…

transformer模型结构|李宏毅机器学习21年

来源&#xff1a;https://www.bilibili.com/video/BV1Bb4y1L7FT?p4&vd_sourcef66cebc7ed6819c67fca9b4fa3785d39 文章目录 概述seq2seqtransformerEncoderDecoderAutoregressive&#xff08;AT&#xff09;self-attention与masked-self attentionmodel如何决定输出的长度…

【亲测有效】支持横竖屏 微信小程序video禁止进度条拖动,微信小程序遮罩进度条,

背景&#xff1a;部分课程禁止客户拖动视频进度条直至播放结束 红色是遮罩区域遮罩区域 实际遮罩效果&#xff08;有一个很浅的阴影区域&#xff09; 实现代码 .wxml文件 <video enable-progress-gesture"false" ><cover-view class"cover">…

基于深度学习的yolov7植物病虫害识别及防治系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介YOLOv7 系统特性工作流程 二、功能三、系统四. 总结 一项目简介 # YOLOv7植物病虫害识别及防治系统介绍 简介 该系统基于深度学习技术&#xff0c;采…

Seata配置

参考教程 seata 分布式事务的环境搭建与使用 Seata 1.4.0 nacos配置和使用&#xff0c;超详细 Seata 1.4.2 的安装 Nacos的配置和使用 官网下载地址 本文以v1.4.1为例 1.数据库及表的创建 创建seata数据库&#xff0c;创建以下表&#xff08;右键连接-》新建数据库seata-》…