当前位置: 首页 > news >正文

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秒后销毁敌人对象}}
http://www.jsqmd.com/news/87308/

相关文章:

  • OpenCV全景拼接终极指南:从零开始快速上手全景图像制作
  • 基于vue的酒店宾馆客房管理系统_6u85gvj9_springboot php python nodejs
  • 云电脑深度玩转CANN:从环境适配到工业级应用落地全指南
  • 150亿参数挑战千亿模型:ServiceNow颠覆企业AI部署范式
  • Nsight Compute精准定位CUDA矩阵乘法性能瓶颈
  • 为什么说Loco+Tauri是2025年桌面应用开发的最佳选择
  • 基于准PR控制的LCL三相并网逆变器仿真模型(带报告) 参考资料:附带自己写的一份报告
  • Web前端入门第 90 问:JavaScript 也能无中生有的创建音频
  • OpenPLC Editor:工业自动化编程的5大核心优势解析
  • Apache Cassandra版本升级:从3.x到4.x的完整迁移实战指南
  • 5分钟搞定AWR1843毫米波雷达:Python实时数据读取与可视化终极指南
  • Dify.AI完整教程:零代码构建专业级AI应用的最佳实践
  • 腾讯开源Hunyuan-4B-Instruct-AWQ-Int4:轻量级大模型开启边缘智能新纪元
  • 基于vue的讲座管理系统设计与实现_1exeip5l_springboot php python nodejs
  • FluidNC运动控制固件:重新定义CNC机器智能化管理 [特殊字符]
  • 大麦抢票终极指南:7.6版本IOS与安卓完整操作手册
  • Qwen3-VL多模态智能终极指南:开启感知与理解的新纪元
  • 规范驱动开发:用 AI 写生产级代码的完整指南
  • Wan2.2开源:如何用AI视频生成重塑内容创作生态
  • 5分钟快速上手wired-elements:打造独特手绘风格组件的神器
  • 视频去水印终极指南:三步轻松去除烦人水印
  • 图书管理系统项目PPT文稿
  • Langflow终极指南:从零构建企业级AI绘画工作流完整方案
  • Packmol分子打包工具:从零开始的完整配置手册
  • 终极PHP压缩工具:Zipper的完整使用指南
  • Docker镜像迁移指南
  • Sponge:Go语言生产力工具的架构解析与实战指南
  • 广告拦截神器uBlock Origin:3大性能优势让你告别90%的网页广告困扰
  • 区块链监控终极指南:构建高效的智能合约运维体系
  • Solon Web 的“分身术”:单应用多端口监听,化身多重服务