当前位置: 首页 > 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.cnnetsun.cn/news/49385.html

相关文章:

  • 【Kubernetes】使用Helm简化k8s部署、管理
  • 零基础也能搭建企业官网:Halo开源建站工具实战指南
  • Open-SaaS邮件系统性能优化实战:构建高并发异步处理架构
  • 基于vue的考研信息共享平台_a5a399ip_springboot php python nodejs
  • ROAPI零代码API构建完整指南:从入门到实战
  • 基于vue的小明餐厅点餐平台的设计_9yzk5cgp_springboot php python nodejs
  • 35、掌握Bash脚本:提升Linux管理效率的秘诀
  • 软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(13)
  • Proxy Audio Device:macOS虚拟音频驱动器的完整指南
  • 终极PHP调试解决方案:用symfony/debug实现高效错误处理
  • 智慧养老项目:当SpringBoot遇到硬件,如何优雅地处理异常与状态管理?
  • 5步轻松搞定AppSmith实时推送:告别消息延迟的终极指南
  • IOPaint终极指南:AI一键去除水印的完整解决方案
  • Windows更新后RDPWrap失效修复指南:快速恢复多用户远程桌面功能
  • GPU和TPU差异之联网
  • 解决 Oracle 监听外网 IP 及腾讯云防火墙配置
  • ORACLE解析游标生成JSON
  • AMD GPU并行通信技术:突破性性能优化实战指南
  • Everywhere AI助手:跨平台智能对话系统深度解析
  • 考古学开放数据中的Paradata研究——CAPTURE项目与文献综述解读
  • 论文解读|将1930年前所有阿拉伯期刊添加到Wikidata——学术众包项目Jarāʾid向数字公共领域的迁移
  • 5分钟掌握UpSetR:超越维恩图的集合交集可视化神器
  • 机构洗盘拼合指标绝无未来 源码分析
  • Android项目架构完整指南:模块化开发与Kotlin最佳实践
  • Horovod Process Sets:让千亿参数模型训练触手可及
  • 5步掌握Loco+Tauri:构建高性能跨平台桌面应用的终极指南
  • 如何在Zephyr RTOS中制定最佳编译策略?
  • 专业实验室改造,必须避开的5大坑
  • 千万注意!实验室装修这5个关键点不容忽视
  • 关于指纹浏览器