news 2026/3/6 4:24:03

Unity教学 项目4 3D求生枪手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity教学 项目4 3D求生枪手

视频教程:

https://www.bilibili.com/video/BV16F7zzqEJF?spm_id_from=333.788.videopod.sections&vd_source=25b783f5f945c4507229e9dec657b5bb

1. 项目初始化

创建项目“ServivalShooter”

导入包“Survival Shooter.unitypackage”

导入环境、灯光预设,创建背景音乐物体,隐藏 Unity 自带灯光

2. 玩家导入、移动

导入玩家

给“Player”添加脚本“PlayerMovement”

usingUnityEngine;/// <summary>/// 控制玩家角色的移动/// </summary>publicclassPlayerMovement:MonoBehaviour{/// <summary>/// 玩家移动速度/// </summary>publicfloatspeed=6f;/// <summary>/// 玩家刚体组件的引用/// </summary>privateRigidbodyrb;/// <summary>/// 在对象激活时获取刚体组件/// </summary>privatevoidAwake(){rb=GetComponent<Rigidbody>();}/// <summary>/// 在固定时间间隔内处理物理移动/// </summary>privatevoidFixedUpdate(){// 获取水平和垂直方向的输入floatmoveHorizontal=Input.GetAxis("Horizontal");floatmoveVertical=Input.GetAxis("Vertical");// 计算移动向量Vector3movement=newVector3(moveHorizontal,0.0f,moveVertical);// 移动玩家到新的位置rb.MovePosition(transform.position+movement*speed*Time.fixedDeltaTime);}}

3. 摄像机跟随

CameraFollow.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCameraFollow:MonoBehaviour{publicfloatsmoothing=5f;privateGameObjectplayer;privateVector3offset;privatevoidAwake(){player=GameObject.FindGameObjectWithTag("Player");}// Start is called before the first frame updatevoidStart(){offset=transform.position-player.transform.position;}// Update is called once per framevoidUpdate(){}privatevoidFixedUpdate(){transform.position=Vector3.Lerp(transform.position,offset+player.transform.position,smoothing*Time.deltaTime);}}

4. 玩家跟随鼠标朝向

设置摄像机

设置地面

PlayerMovement.cs

usingUnityEngine;/// <summary>/// 控制玩家角色的移动和旋转/// </summary>publicclassPlayerMovement:MonoBehaviour{/// <summary>/// 玩家移动速度(单位:米/秒)/// </summary>publicfloatspeed=6f;/// <summary>/// 玩家刚体组件的引用,用于物理移动/// </summary>privateRigidbodyrb;/// <summary>/// 在脚本激活时初始化,获取刚体组件/// </summary>privatevoidAwake(){// 获取附加到同一游戏对象的Rigidbody组件rb=GetComponent<Rigidbody>();}/// <summary>/// 固定时间间隔调用的物理更新方法(默认每秒50次)/// </summary>privatevoidFixedUpdate(){// 获取水平和垂直方向的输入(WASD或方向键)floath=Input.GetAxis("Horizontal");floatv=Input.GetAxis("Vertical");// 处理移动和旋转Move(h,v);// 根据输入移动玩家Turning();// 使玩家朝向鼠标位置}/// <summary>/// 根据输入移动玩家/// </summary>/// <param name="h">水平输入(-1到1)</param>/// <param name="v">垂直输入(-1到1)</param>voidMove(floath,floatv){// 创建移动向量(忽略Y轴,因为是在平面上移动)Vector3movement=newVector3(h,0.0f,v);// 使用刚体移动玩家位置,确保物理效果正确// Time.fixedDeltaTime确保移动速度与帧率无关rb.MovePosition(transform.position+movement*speed*Time.fixedDeltaTime);}/// <summary>/// 使玩家朝向鼠标所在位置/// </summary>voidTurning(){// 从主摄像机创建一条射线,方向指向鼠标屏幕位置RaycameraRay=Camera.main.ScreenPointToRay(Input.mousePosition);// 获取地面层的图层,用于射线检测intfloorLayer=LayerMask.GetMask("Ground");RaycastHitfloorHit;// 发射射线检测地面碰撞boolisTouchFloor=Physics.Raycast(cameraRay,outfloorHit,100,floorLayer);if(isTouchFloor){// 计算从玩家位置到鼠标点击位置的向量Vector3playerToMouse=floorHit.point-transform.position;// 确保玩家只在水平面上旋转(Y轴设为0)playerToMouse.y=0;// 创建目标旋转(使玩家朝向鼠标位置)QuaterniontargetRotation=Quaternion.LookRotation(playerToMouse);// 应用旋转到刚体rb.MoveRotation(targetRotation);}}}

5. 静置、移动动画

增加动画控制器

设置静置动画、移动动画切换

增加参数 isWalking

静置切换为移动

移动切换为静置

编辑代码 PlayerMovement.cs

使用控制器改变 isWalking 参数值进而控制动画切换

usingUnityEngine;/// <summary>/// 控制玩家角色的移动/// </summary>publicclassPlayerMovement:MonoBehaviour{/// <summary>/// 玩家移动速度/// </summary>publicfloatspeed=6f;/// <summary>/// 玩家刚体组件的引用/// </summary>privateRigidbodyrb;/// <summary>/// 玩家动画组件的引用/// </summary>privateAnimatoranim;/// <summary>/// 在对象激活时获取刚体和动画组件/// </summary>privatevoidAwake(){rb=GetComponent<Rigidbody>();anim=GetComponent<Animator>();}/// <summary>/// 在固定时间间隔内处理物理移动、旋转和动画/// </summary>privatevoidFixedUpdate(){// 获取水平和垂直方向的输入floath=Input.GetAxis("Horizontal");floatv=Input.GetAxis("Vertical");// 移动角色Move(h,v);// 角色朝向鼠标指向位置Turing();// 播放行走动画Animating(h,v);}/// <summary>/// 根据输入移动角色/// </summary>/// <param name="h">水平输入</param>/// <param name="v">垂直输入</param>voidMove(floath,floatv){// 计算移动向量Vector3movement=newVector3(h,0.0f,v);// 移动玩家到新的位置rb.MovePosition(transform.position+movement*speed*Time.fixedDeltaTime);}/// <summary>/// 让角色朝向鼠标指向的地面位置/// </summary>voidTuring(){// 创建一条从主摄像机到鼠标位置的射线RaycameraRay=Camera.main.ScreenPointToRay(Input.mousePosition);// 获取地面层的LayerMaskintfloorLayer=LayerMask.GetMask("Ground");RaycastHitfloorHit;// 射线检测是否击中地面boolisTouchFloor=Physics.Raycast(cameraRay,outfloorHit,100,floorLayer);if(isTouchFloor){// 计算角色到鼠标点击点的方向向量Vector3v3=floorHit.point-transform.position;v3.y=0;// 保持水平方向// 计算朝向该方向的旋转Quaternionq=Quaternion.LookRotation(v3);// 旋转角色rb.MoveRotation(q);}}/// <summary>/// 根据输入设置动画参数,控制行走动画/// </summary>/// <param name="h">水平输入</param>/// <param name="v">垂直输入</param>voidAnimating(floath,floatv){// 判断是否有输入boolisWalking=h!=0||v!=0;// 设置动画参数anim.SetBool("isWalking",isWalking);}}

6. 发射效果

声音

灯光

粒子效果

新建脚本挂载

MyPlayerShooting.cs

usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;/// <summary>/// 控制玩家射击行为的脚本/// </summary>publicclassMyPlayerShooting:MonoBehaviour{/// <summary>/// 枪械音效组件/// </summary>privateAudioSourcegunAudio;/// <summary>/// 距离上次射击经过的时间/// </summary>privatefloatshootTime=0f;/// <summary>/// 射击间隔(秒)/// </summary>privatefloatshootDelay=0.15f;/// <summary>/// 枪口闪光持续时间比例/// </summary>privatefloatlightRate=0.2f;/// <summary>/// 枪口闪光灯组件/// </summary>privateLightshootLight;/// <summary>/// 射击线渲染组件/// </summary>privateLineRenderershootLine;/// <summary>/// 射击粒子特效组件/// </summary>privateParticleSystemshootParticleSystem;/// <summary>/// 获取所需组件的引用/// </summary>privatevoidAwake(){gunAudio=GetComponent<AudioSource>();shootLight=GetComponent<Light>();shootLine=GetComponent<LineRenderer>();shootParticleSystem=GetComponent<ParticleSystem>();}// Start 在游戏开始前调用(此处未使用)voidStart(){}/// <summary>/// 每帧更新,处理射击逻辑和特效显示/// </summary>voidUpdate(){// 累加时间shootTime+=Time.deltaTime;// 检测射击输入且满足射击间隔if(Input.GetButton("Fire1")&&shootTime>=shootDelay){Shooting();}// 控制枪口闪光和射线显示时间if(shootTime>=shootDelay*lightRate){shootLight.enabled=false;shootLine.enabled=false;}}/// <summary>/// 执行射击操作,包括音效、特效和射线显示/// </summary>privatevoidShooting(){// 重置射击计时shootTime=0;// 播放射击音效gunAudio.Play();// 显示枪口闪光shootLight.enabled=true;// 设置射线起点为枪口位置shootLine.SetPosition(0,transform.position);// 设置射线终点为前方100单位shootLine.SetPosition(1,transform.position+transform.forward*100);// 显示射线shootLine.enabled=true;// 播放粒子特效shootParticleSystem.Play();}}

7. 添加敌人

添加敌人模型,并添加相应组件

设置组件

安装“AI Navigation”包

设置并烘焙可导航区域

编写脚本 MyEnemyMovement.cs,实现敌人跟随玩家

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.AI;publicclassMyEnemyMovement:MonoBehaviour{privateTransformplayer;privateNavMeshAgentnav;privatevoidAwake(){player=GameObject.FindGameObjectWithTag("Player").transform;nav=GetComponent<NavMeshAgent>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){nav.SetDestination(player.position);}}

添加并设置移动动画

8. 攻击敌人

8.1. 线条绘制

射线命中敌人,射线止于敌人处,未命中敌人则射出 100 个单位长度

设置敌人图层

编写设计代码 MyPlayerShooting.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMyPlayerShooting:MonoBehaviour{privatefloatshootDelay=0.15f;//发射延迟privatefloatshootTime=0f;//发射时间privatefloatshootRate=0.2f;//发射关闭效果延迟比例privateLightshootLight;//发射灯光privateLineRenderershootLine;//发射激光射线privateParticleSystemshootParticle;//发射粒子效果privateAudioSourceshootAduio;//枪声音//开枪发射射线相关的变量privateRayshootRay;privateRaycastHitshootHit;privateintshootMask;privatevoidAwake(){shootLight=GetComponent<Light>();shootLine=GetComponent<LineRenderer>();shootParticle=GetComponent<ParticleSystem>();shootAduio=GetComponent<AudioSource>();shootMask=LayerMask.GetMask("Shootable");}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){//上次发射后经过时间shootTime=shootTime+Time.deltaTime;//如果点击鼠标左键且满足发射延迟则进行发射if(Input.GetButton("Fire1")&&shootTime>=shootDelay){Shooting();}//经过0.03秒则关闭发射效果//if(shootTime >= shootDelay * shootRate)if(shootTime>=0.03){//关闭发射灯光效果shootLight.enabled=false;//关闭发射射线效果shootLine.enabled=false;}}voidShooting(){shootTime=0f;//开启发射灯光效果shootLight.enabled=true;//设置发射射线位置shootLine.SetPosition(0,transform.position);shootLine.SetPosition(1,transform.position+100*transform.forward);//开启发射射线效果shootLine.enabled=true;//播放发射粒子效果shootParticle.Play();//播放发射声音shootAduio.Play();//发射射线,检测是否击中敌人shootRay.origin=transform.position;shootRay.direction=transform.forward;//如果击中敌人,则获取敌人脚本并调用其受伤方法if(Physics.Raycast(shootRay,outshootHit,100,shootMask)){shootLine.SetPosition(1,shootHit.point);}else{//如果没有击中敌人,则设置射线终点为100米远shootLine.SetPosition(1,transform.position+100*transform.forward);}}}

8.2. 声音、特效

敌人物体添加组件血量脚本、被攻击粒子特效、被攻击声音

编写脚本 MyEnemyHealth.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMyEnemyHealth:MonoBehaviour{publicinthp=100;privateParticleSystemenemyParticleSystem;privateAudioSourceaudioSource;privatevoidAwake(){enemyParticleSystem=GetComponent<ParticleSystem>();audioSource=GetComponent<AudioSource>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){}publicvoidTakeDamage(intdamage){hp-=damage;// 减少生命值audioSource.Play();// 播放受伤音效enemyParticleSystem.transform.position=transform.position;// 设置粒子效果位置enemyParticleSystem.Play();// 播放粒子效果}}

修改脚本 MyPlayerShooting.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMyPlayerShooting:MonoBehaviour{privatefloatshootDelay=0.15f;//发射延迟privatefloatshootTime=0f;//发射时间privatefloatshootRate=0.2f;//发射关闭效果延迟比例privateLightshootLight;//发射灯光privateLineRenderershootLine;//发射激光射线privateParticleSystemshootParticle;//发射粒子效果privateAudioSourceshootAduio;//枪声音//开枪发射射线相关的变量privateRayshootRay;privateRaycastHitshootHit;privateintshootMask;privatevoidAwake(){shootLight=GetComponent<Light>();shootLine=GetComponent<LineRenderer>();shootParticle=GetComponent<ParticleSystem>();shootAduio=GetComponent<AudioSource>();shootMask=LayerMask.GetMask("Shootable");}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){//上次发射后经过时间shootTime=shootTime+Time.deltaTime;//如果点击鼠标左键且满足发射延迟则进行发射if(Input.GetButton("Fire1")&&shootTime>=shootDelay){Shooting();}//经过0.03秒则关闭发射效果//if(shootTime >= shootDelay * shootRate)if(shootTime>=0.03){//关闭发射灯光效果shootLight.enabled=false;//关闭发射射线效果shootLine.enabled=false;}}voidShooting(){shootTime=0f;//开启发射灯光效果shootLight.enabled=true;//设置发射射线位置shootLine.SetPosition(0,transform.position);shootLine.SetPosition(1,transform.position+100*transform.forward);//开启发射射线效果shootLine.enabled=true;//播放发射粒子效果shootParticle.Play();//播放发射声音shootAduio.Play();//发射射线,检测是否击中敌人shootRay.origin=transform.position;shootRay.direction=transform.forward;//如果击中敌人,则获取敌人脚本并调用其受伤方法if(Physics.Raycast(shootRay,outshootHit,100,shootMask)){shootLine.SetPosition(1,shootHit.point);MyEnemyHealthenemyHealth=shootHit.collider.GetComponent<MyEnemyHealth>();enemyHealth.TakeDamage(10);//假设每次射击造成10点伤害}else{//如果没有击中敌人,则设置射线终点为100米远shootLine.SetPosition(1,transform.position+100*transform.forward);}}}

8.3. 受到伤害

收到玩家攻击,血量减少,血量为 0 时播放死亡动画,死亡后不能动

MyEnemyHealth.cs

usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMyEnemyHealth:MonoBehaviour{publicinthp=100;publicAudioClipdeathClip;privateParticleSystemenemyParticleSystem;privateAudioSourceaudioSource;privateAnimatoranimator;privateCapsuleCollidercapsuleCollider;privatevoidAwake(){enemyParticleSystem=GetComponent<ParticleSystem>();audioSource=GetComponent<AudioSource>();animator=GetComponent<Animator>();capsuleCollider=GetComponent<CapsuleCollider>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){}publicvoidTakeDamage(intdamage){hp-=damage;// 减少生命值if(hp<=0)Death();audioSource.Play();// 播放受伤音效enemyParticleSystem.transform.position=transform.position;// 设置粒子效果位置enemyParticleSystem.Play();// 播放粒子效果}privatevoidDeath(){hp=0;// 确保生命值不小于0audioSource.clip=deathClip;audioSource.Play();animator.SetTrigger("death");capsuleCollider.enabled=false;// 禁用碰撞体}publicvoidStartSinking(){}}

MyEnemyMovement.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.AI;publicclassMyEnemyMovement:MonoBehaviour{privateTransformplayer;privateNavMeshAgentnav;privateMyEnemyHealthenemyHealth;privatevoidAwake(){player=GameObject.FindGameObjectWithTag("Player").transform;nav=GetComponent<NavMeshAgent>();enemyHealth=GetComponent<MyEnemyHealth>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){if(enemyHealth.hp<=0)return;// 如果敌人已死亡,则不进行移动nav.SetDestination(player.position);}}

8.4. 死亡

MyEnemyHealth.cs

usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.AI;publicclassMyEnemyHealth:MonoBehaviour{publicinthp=100;publicAudioClipdeathClip;privateParticleSystemenemyParticleSystem;privateAudioSourceaudioSource;privateAnimatoranimator;privateCapsuleCollidercapsuleCollider;privateboolisSinking=false;privatevoidAwake(){enemyParticleSystem=GetComponent<ParticleSystem>();audioSource=GetComponent<AudioSource>();animator=GetComponent<Animator>();capsuleCollider=GetComponent<CapsuleCollider>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){if(isSinking){transform.Translate(Vector3.down*Time.deltaTime*2f);// 向下移动,模拟沉没效果}}publicvoidTakeDamage(intdamage){hp-=damage;// 减少生命值if(hp<=0)Death();audioSource.Play();// 播放受伤音效enemyParticleSystem.transform.position=transform.position;// 设置粒子效果位置enemyParticleSystem.Play();// 播放粒子效果}privatevoidDeath(){hp=0;// 确保生命值不小于0audioSource.clip=deathClip;// 设置死亡音效audioSource.Play();// 播放死亡音效animator.SetTrigger("death");// 设置死亡动画触发器capsuleCollider.enabled=false;// 禁用胶囊碰撞器GetComponent<Rigidbody>().isKinematic=true;// 设置刚体为静态GetComponent<NavMeshAgent>().enabled=false;// 禁用导航网格代理StartSinking();}publicvoidStartSinking(){isSinking=true;Destroy(gameObject,2f);// 2秒后销毁敌人对象}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/28 14:12:30

【Kubernetes】使用Helm简化k8s部署、管理

确定你安装版本的安全配置安装和配置Helm。安装HelmmacOSbrew install helmWindowschoco install kubernetes-helmDebian/Ubuntusudo apt-get install curl gpg apt-transport-https --yescurl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --de…

作者头像 李华
网站建设 2026/3/1 12:31:01

零基础也能搭建企业官网:Halo开源建站工具实战指南

你是否在为创业初期的线上形象而烦恼&#xff1f;面对高昂的SaaS建站费用和技术门槛&#xff0c;很多创业者望而却步。本文将通过Halo开源建站工具&#xff0c;为你提供一套完整的解决方案&#xff0c;让你在30分钟内搭建专业企业官网&#xff0c;实现零成本起步。 【免费下载链…

作者头像 李华
网站建设 2026/3/5 13:58:50

Open-SaaS邮件系统性能优化实战:构建高并发异步处理架构

Open-SaaS邮件系统性能优化实战&#xff1a;构建高并发异步处理架构 【免费下载链接】open-saas A free, open-source SaaS app starter for React & Node.js with superpowers. Production-ready. Community-driven. 项目地址: https://gitcode.com/GitHub_Trending/op/…

作者头像 李华
网站建设 2026/3/2 11:13:49

基于vue的考研信息共享平台_a5a399ip_springboot php python nodejs

目录具体实现截图项目介绍论文大纲核心代码部分展示项目运行指导结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持java、ThinkPHP、Node.js、Spring B…

作者头像 李华
网站建设 2026/3/5 15:14:43

ROAPI零代码API构建完整指南:从入门到实战

ROAPI零代码API构建完整指南&#xff1a;从入门到实战 【免费下载链接】roapi Create full-fledged APIs for slowly moving datasets without writing a single line of code. 项目地址: https://gitcode.com/gh_mirrors/ro/roapi 你是否曾为数据文件创建API而烦恼&…

作者头像 李华
网站建设 2026/3/6 4:02:34

基于vue的小明餐厅点餐平台的设计_9yzk5cgp_springboot php python nodejs

目录具体实现截图项目介绍论文大纲核心代码部分展示项目运行指导结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持java、ThinkPHP、Node.js、Spring B…

作者头像 李华