Unity太空避障Demo总结

太空避障:主要是实现飞机躲避子弹
面板基类、音乐类、排行榜类、json等等都和上一篇Unity坦克迷宫Demo总结一样,太空避障主要是对四元数的练习和使用
1.选择飞机面板
(1)通过左右按钮对显示的模型进行切换
(2)通过点击鼠标左键可以实现对飞机的拖动 观察飞机的整体
知识点
(1)四元数 * 四元数:会得到一个新的四元数,大多数用于旋转量相乘,相当于旋转(这个旋转的坐标是物体自身的坐标系)
(2)四元数 * 向量:会得到一个新的向量,对一个向量进行一定旋转,相当于旋转向量

public class SelectPanel : BasePanel<SelectPanel>
{public Button btnRight;public Button btnLeft;public Button btnStart;public Button btnClose;//标识当前已经创建的飞机 当切换时要删除该飞机private GameObject nowAirObj;//飞机的父物体位置public Transform airPos;//飞机的属性列表 通过隐藏和显示GameObject实现public GameObject[] hpList;public GameObject[] speedList;public GameObject[] voluemeList;public override void Init(){//在GameDataManager中保存了当前选中的飞机id 默认从0开始 点右按钮++ 左按钮-- 还有对边界的判断btnRight.onClick.AddListener(() =>{++GameDataManager.Instance.nowSelectAir;if(GameDataManager.Instance.nowSelectAir > GameDataManager.Instance.airData.Count - 1)GameDataManager.Instance.nowSelectAir = 0;ChangAir();});btnLeft.onClick.AddListener(() =>{--GameDataManager.Instance.nowSelectAir;if (GameDataManager.Instance.nowSelectAir < 0)GameDataManager.Instance.nowSelectAir = GameDataManager.Instance.airData.Count - 1;ChangAir();});btnStart.onClick.AddListener(() =>{SceneManager.LoadScene("GameScene");});btnClose.onClick.AddListener(() =>{BeginPanel.Instance.ShowPanel();HidePanel();});HidePanel();}//重写了隐藏和显示方法 这样就可以同时实现一些逻辑public override void ShowPanel(){base.ShowPanel();GameDataManager.Instance.nowSelectAir = 0;ChangAir();}public override void HidePanel(){base.HidePanel();DestroyObj();}public void ChangAir(){//删除上个飞机 创建新飞机 并记录DestroyObj();AirData airData = GameDataManager.Instance.airData[GameDataManager.Instance.nowSelectAir];nowAirObj = Instantiate(Resources.Load<GameObject>(airData.res));nowAirObj.transform.SetParent(airPos.transform, false);for(int i = 0; i < hpList.Length;i++){hpList[i].SetActive(i < airData.hp);speedList[i].SetActive(i < airData.speed);voluemeList[i].SetActive(i < airData.volume);}}public void DestroyObj(){if(nowAirObj != null){Destroy(nowAirObj);}}private float time;//点中可以进行拖动public bool isSelect;private void Update(){//让飞机上下缓缓飞(展示作用)通过sin函数 乘内测的数代表飞的频率 乘外侧的数代表飞的位移//基于世界坐标 飞机的展示面会倾斜 基于自己坐标是错的time += Time.deltaTime;this.airPos.Translate(Vector3.up * Mathf.Sin(time) * 0.001f,Space.World);//鼠标左键实现拖动 通过射线检测 将飞机的旋转角度以y旋转 度数为Mouse X * 10度if(Input.GetMouseButtonDown(0)){if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), 1000, 1 << LayerMask.NameToLayer("Air"))){isSelect = true;}}if(Input.GetMouseButtonUp(0)){isSelect = false;}if(isSelect && Input.GetMouseButton(0)){airPos.rotation *= Quaternion.AngleAxis(Input.GetAxis("Mouse X") * 10,Vector3.up);}}
}

2.玩家类(飞机超出屏幕问题未解决)
(1)当飞机左右移动时,需要有一定的倾斜,停止时恢复
(2)玩家类需要受伤 死亡 移动方法
知识点
(1)射线检测时销毁碰撞体(子弹)

public class PlayerObj : MonoBehaviour
{//写成一个单例类private static PlayerObj instance;public static PlayerObj Instance => instance;//maxHp代表最大血量 nowHp代表变量public int nowHp;public int maxHp;public int moveSpeed;public int rotaSpeed;//是否死亡public bool isDead;//目标四元数旋转角度Quaternion targetQ;private float hValue;private float vValue;//世界坐标的点 没有去屏幕外的点private Vector3 frontPos = new Vector3(-41,0,23);private Vector3 frontPos2 = new Vector3(41, 0, -23);//世界坐标转换为屏幕坐标的点private Vector3 nowPos;private void Awake(){instance = this;}void Update(){if (isDead)return;//GetAxisRaw 不是渐变 -1 1两个数//adhValue = Input.GetAxisRaw("Horizontal");//wsvValue = Input.GetAxisRaw("Vertical");//为0时说明静止if (hValue == 0)targetQ = Quaternion.identity;else//不为0时 按左键值会小于0 右键值会大于0 决定一下旋转的方向//可以得到一个目标的四元数targetQ = hValue < 0 ? Quaternion.AngleAxis(20, Vector3.forward) : Quaternion.AngleAxis(-20, Vector3.forward);//趋近于目标四元数this.transform.rotation = Quaternion.Slerp(this.transform.rotation, targetQ,Time.deltaTime * rotaSpeed);//正常移动this.transform.Translate(Vector3.forward * vValue * Time.deltaTime * moveSpeed);//左右移动不能使用自身坐标系 越动越会往下掉 要使用世界this.transform.Translate(Vector3.right * hValue * Time.deltaTime * moveSpeed,Space.World);//当前坐标的屏幕坐标点this.nowPos = Camera.main.WorldToScreenPoint(this.transform.position);//******************临时判断屏幕边缘的逻辑 当分辨率变化时会出错 暂未解决******************if (nowPos.x <= 0){//只管理x 不管理yzthis.transform.position = new Vector3(frontPos.x, this.transform.position.y, this.transform.position.z);}if (nowPos.y <= 0){this.transform.position = new Vector3(this.transform.position.x, this.transform.position.y, frontPos2.z);}if(nowPos.x >= Screen.width){this.transform.position = new Vector3(frontPos2.x, this.transform.position.y, this.transform.position.z);}if (nowPos.y >= Screen.height){this.transform.position = new Vector3(this.transform.position.x, this.transform.position.y, frontPos.z);}//获取碰到的对象的信息RaycastHit hit;if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 1000, 1 << LayerMask.NameToLayer("Bullet"))){//通过hit可以得到他身上所有的信息BulletObj bullet = hit.transform.GetComponent<BulletObj>();bullet.Dead();}}public void Wound(){this.nowHp -= 1;Debug.Log("当前血量" + nowHp);GamePanel.Instance.ChangHp(nowHp);if(nowHp <= 0){nowHp = 0;Dead();}}public void Dead(){isDead = true;EndPanel.Instance.ShowPanel();}
}

3.子弹类
(1)子弹的初始化数据 移动方式 生命周期 销毁以及碰撞检测

public class BulletObj : MonoBehaviour
{private BulletData data;private float time;/// <summary>/// 一个初始化方法 创建子弹时调用 传入目标数据/// </summary>/// <param name="id"></param>public void Init(int id){data = GameDataManager.Instance.bulletDatas[id - 1];//生命周期 到一定时间自动销毁子弹Invoke("DelayDestroy", data.destroyTime);}//startPos代表起始位置 下面做四元数匀速运动用的private void Awake(){startPos = this.transform;}//销毁子弹 创建特效public void Dead(){GameObject effObj = Instantiate(Resources.Load<GameObject>(data.effRes));effObj.transform.position = this.transform.position;Destroy(effObj,0.5f);Destroy(this.gameObject);}//碰撞检测public void OnTriggerEnter(Collider other){if(other.CompareTag("Player")){if(!PlayerObj.Instance.isDead){PlayerObj obj = other.GetComponent<PlayerObj>();obj.Wound();Dead();}else{this.Dead();}}}//不确定子弹会不会提前被移除掉 所以使用延迟函数 当子弹已经被移除的时候 就不会执行了public void DelayDestroy(){GameObject effObj = Instantiate(Resources.Load<GameObject>(data.effRes));effObj.transform.position = this.transform.position;Destroy(effObj, 0.5f);Destroy(this.gameObject);}//时间标识 做匀速运动用private float uniform;private Transform startPos;// Update is called once per framevoid Update(){//共同的特征 面朝向移动this.transform.Translate(Vector3.forward * data.forwardSpeed * Time.deltaTime);//1 直线运动//2 曲线运动//3 左抛物线//4 右抛物线//5 跟踪switch (data.type){case 2://sin里面的值 决定 左右变化的频率//sin后面的值 决定 左右位移的多少time += Time.deltaTime;this.transform.Translate(Vector3.right * Mathf.Sin(time * data.rightSpeed) * data.rotaSpeed * Time.deltaTime);break;case 3:this.transform.rotation *= Quaternion.AngleAxis(-data.rotaSpeed * Time.deltaTime,Vector3.up);break;case 4:this.transform.rotation *= Quaternion.AngleAxis(data.rotaSpeed * Time.deltaTime, Vector3.up);break;case 5://先快后慢 出现问题 "子弹无线接近目标旋转位置过远" 那要看一下是不是子弹的位置没有归0//这里使用匀速uniform += Time.deltaTime;Quaternion q = Quaternion.LookRotation(PlayerObj.Instance.transform.position - this.transform.position);this.transform.rotation = Quaternion.Slerp(this.startPos.rotation,q, uniform * data.rotaSpeed);break;}}
}

4.炮口点
(1)炮口的位置为上方三点 中间左右两点 下方三点
(2)炮口的初始角度(为了方便开火点四元数的计算)
(3)上下左右四个点的旋转角度是180度,四个角旋转角度都是90度,这样子弹才能合理地发射在屏幕内

public enum Type_PointType
{topLeft,topRight,topCenter,Left,Right,bottomLeft,bottomRight,bottomCenter
}
public class PointObj : MonoBehaviour
{public Type_PointType type = new Type_PointType();private Vector3 pos;//炮口的初始方向private Vector3 initPos;//炮口去拿到配置表中的数据 方便使用private int num;private float offset;private float delay;//全部的子弹数据List<BulletData> bullets;//单个开火数据public FireData fire;//单个子弹数据public BulletData bullet;// Update is called once per framevoid Update(){//初始化各个炮口的位置UpdatePos();//每次发射子弹的随机数 以及旋转角度的设置Reset();//发射子弹Fire();}public void UpdatePos(){//我们需要找到在z这一层的切面 因为其他子弹也需要在这一层//先根据玩家的世界坐标找到屏幕坐标的z是多少//然后设置世界坐标的z给pos//最后用屏幕转为世界坐标 让子弹出现的位置都在这一层即可pos.z = 40;switch(type){case Type_PointType.topLeft:pos.x = 0;pos.y = Screen.height;initPos = Vector3.right;break;case Type_PointType.topRight:pos.x = Screen.width;pos.y = Screen.height;initPos = Vector3.left;break;case Type_PointType.topCenter:pos.x = Screen.width/2;pos.y = Screen.height;initPos = Vector3.right;break;case Type_PointType.Left:pos.x = 0;pos.y = Screen.height/2;initPos = Vector3.right;break;case Type_PointType.Right:pos.x = Screen.width;pos.y = Screen.height/2;initPos = Vector3.left;break;case Type_PointType.bottomLeft:pos.x = 0;pos.y = 0;initPos = Vector3.right;break;case Type_PointType.bottomRight:pos.x = Screen.width;pos.y = 0;initPos = Vector3.left;break;case Type_PointType.bottomCenter:pos.x = Screen.width/2;pos.y = 0;initPos = Vector3.right;break;}this.transform.position = Camera.main.ScreenToWorldPoint(pos);}//旋转的角度private float angle;/// <summary>/// /// 随机数 将子弹的四种移动方式和开火的两种方式随机排列组合 想实现的功能看个人/// bullet 四种类型1.直线 2.曲线 3.左抛物线 4.右抛物线 5.跟踪  /// fire 两种类型1.连发 2.散弹/// </summary>public void Reset(){//终止条件的判断 如果子弹间隔时间和数量没有发完 不能从新随机if (offset != 0 && num != 0)return;if(fire != null){//delay代表每组子弹的间隔时间delay -= Time.deltaTime;if(delay > 0){return;}}//拿到全部的开火数据 随机选择开火方式List<FireData> fires = GameDataManager.Instance.fireData;fire = fires[Random.Range(0, fires.Count)];//本地拿到初始值this.num = fire.num;this.offset = fire.offset;this.delay = fire.delay;//拿到全部的子弹数据 随机选择子弹移动方式bullets = GameDataManager.Instance.bulletDatas;bullet = bullets[Random.Range(0, bullets.Count)];//1.左上角和右下角发射右抛物线//2.左下角和右上角发射左抛物线//3.type = 3 是左抛物线 type = 4 是右抛物线if(fire.type == 2){//2类型是散弹 需要随机角度switch (type){case Type_PointType.topLeft:case Type_PointType.bottomLeft:case Type_PointType.topRight:case Type_PointType.bottomRight:angle = 90 / num;break;case Type_PointType.topCenter:case Type_PointType.Left:case Type_PointType.Right:case Type_PointType.bottomCenter:angle = 180 / num;break;}}}private Vector3 newRot;/// <summary>/// 开火 个人决定开火口能发射怎样的子弹/// 核心点:根据不同的开火方式 去创建子弹 以及子弹特效 子弹的位置 子弹的旋转角度/// </summary>public void Fire(){if (type == Type_PointType.topLeft || type == Type_PointType.bottomRight){bullet.type = 4;}if (type == Type_PointType.topRight || type == Type_PointType.bottomLeft){bullet.type = 3;}//间隔时间和数量归0 不会进入循环if (offset == 0 && num == 0)return;//每个子弹的判断放到最前面offset -= Time.deltaTime;//当间隔时间大于0 还在等待期 if (offset > 0)return;GameObject go;switch (fire.type){case 1:go = Instantiate(Resources.Load<GameObject>(bullet.bulletRes));go.GetComponent<BulletObj>().Init(bullet.id);go.transform.position = this.transform.position;go.transform.rotation = Quaternion.LookRotation(PlayerObj.Instance.transform.position - this.transform.position);//子弹减少--num;//当子弹发射完成后 间隔时间归0 就不会再次进入循环 没发射完就重置间隔时间offset = num == 0 ? 0 : fire.offset;break;case 2://散弹不能发射曲线子弹if (bullet.type != 2){for (int i = 0; i < num; i++){go = Instantiate(Resources.Load<GameObject>(bullet.bulletRes));go.GetComponent<BulletObj>().Init(bullet.id);go.transform.position = this.transform.position;//四元数乘向量 得到新的向量//每次旋转i * 20 newRot = Quaternion.AngleAxis(i * angle, Vector3.up) * initPos;//新的方向给四元数 最后赋值给角度即可go.transform.rotation = Quaternion.LookRotation(newRot);}offset = num = 0;}else{Reset();}break;}}
}

5.Main调用
(1)最后通过Main放置在游戏场景,当进入场景时,根据选择的id创建飞机,初始化变量,更新界面的ui,其实放在GamePanel中也是一样的,都可以

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

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

相关文章

科技未来·无限可能“2024世亚智博会”

随着科技的飞速发展&#xff0c;人类社会正以前所未有的速度迈向一个全新的时代。科学技术作为第一生产力&#xff0c;不仅极大地推动了经济和社会的发展&#xff0c;更在不断地改变着我们的生活方式和思维方式。特别是在人工智能、物联网等前沿科技领域&#xff0c;其创新和应…

【ZYNQ】VDMA 的介绍

AXI VDMA 是 Xilinx 官方提供的高带宽视频 DMA IP&#xff0c;用于实现 AXI4-Stream 视频数据流与 AXI4 接口数据的转换&#xff0c;同时提供帧缓存与帧同步控制功能。本文主要介绍 AXI VDMA 的基本结构与原理&#xff0c;并简要介绍 VDMA 的配置与使用方法。 目录 1 VDMA 简介…

egg代码生成器

今天给大家分享一下egg的代码生成器&#xff0c;这个其实原理很简单&#xff0c;说白了就是用到了nodejs的一个文件io的操作&#xff0c;通过一系列配置参数解析然后生成一个很长的字符串&#xff0c;写入到文件中&#xff0c;最后导出到我们指定的文件夹。 前提概要 为什么我…

web前端——VUE

1.什么是框架&#xff1f; ①概述 框架结构就是基本功能&#xff0c;把很多基础功能已经实现了、封装了。在基础语言之上&#xff0c;对各种基础功能进行封装&#xff0c;方便开发者&#xff0c;提高开发效率 ②前端框架 javaScript是原生的 vue.js&#xff1a; 是一个js框架&…

【Java核心技术16】Java异常处理机制:深入探索与实践

Java异常处理机制&#xff1a;深入探索与实践 所有文章均为原创验证&#xff0c;您随手的 关注、点赞、收藏 是我创作最大的动力。 在软件开发中&#xff0c;异常处理是一个不可或缺的部分&#xff0c;它允许程序在遇到错误时能够优雅地处理&#xff0c;而不是直接崩溃。在Java…

最新解决docker镜像无法下载问题

1.增加或修改daemon.json文件 ​ cd /etc/dockervi daemon.json{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }2.重启docker服务 sudo systemctl daemon-reload sudo systemctl restart docker 3.验证 下载https://txodoo.cn/blog/11/d…

双指针dd d df f

像二分这样的算法&#xff0c;我们甚至可以不用管&#xff0c;直接在问题空间之内搜索&#xff0c;但是双指针也非常好用&#xff0c;帮助我们来减少枚举对象&#xff0c;我们来总结一下这经典的三个题目&#xff1a; 最长上升不重复子序列活动 - AcWings 首先一定要写…

LabVIEW电路板故障诊断系统

基于LabVIEW软件开发的电路板故障诊断系统&#xff0c;涵盖功能测试、性能测试和通讯测试等多个方面。系统集成了多种硬件设备&#xff0c;包括NI PXI-1033机箱、NI PXI-4071数字万用表、NI PXI-4130电源模块、NI PXI-8512 CAN模块等&#xff0c;通过模块化设计实现了对电路板的…

AVL许可证适用范围

在当今数字化时代&#xff0c;软件已经成为企业运营不可或缺的组成部分。然而&#xff0c;不同的软件应用需要相应的许可证才能正常使用。AVL许可证作为其中一种常见的软件许可证&#xff0c;其适用范围广泛&#xff0c;能够帮助企业充分释放软件的潜能。本文将深入探讨AVL许可…

shell中的函数

统计家目录下.c文件的个数 终端输入一个.sh文件&#xff0c;判断文件是否由可执行权限&#xff0c;如果有可执行权限运行脚本&#xff0c;没有可执行权限添加可执行权限后&#xff0c;再运行脚本 写一个函数&#xff0c;输出当前用户uid和gid&#xff0c;并使用变量接收结果 终…

【LLM Transparency Tool】用于深入分析和理解大型语言模型(LLM)工作原理的工具

背景 LLM Transparency Tool 是一个用于深入分析和理解大型语言模型&#xff08;LLM&#xff09;工作原理的工具&#xff0c;旨在增加这些复杂系统的透明度。它提供了一个交互式界面&#xff0c;用户可以通过它观察、分析模型对特定输入&#xff08;prompts&#xff09;的反应…

加速科技Flash存储测试解决方案 全面保障数据存储可靠性

Flash存储芯片 现代电子设备的核心数据存储守护者 Flash存储芯片是一种关键的非易失性存储器&#xff0c;作为现代电子设备中不可或缺的核心组件&#xff0c;承载着数据的存取重任。这种小巧而强大的芯片&#xff0c;以其低功耗、可靠性、高速的读写能力和巨大的存储容量&…

OpenGL-ES 学习(6)---- 立方体绘制

目录 立方体绘制基本原理立方体的顶点坐标和绘制顺序立方体颜色和着色器实现效果和参考代码 立方体绘制基本原理 一个立方体是由8个顶点组成&#xff0c;共6个面&#xff0c;所以绘制立方体本质上就是绘制这6个面共12个三角形 顶点的坐标体系如下图所示&#xff0c;三维坐标…

【目标检测】Yolov8 完整教程 | 检测 | 计算机视觉

学习资源&#xff1a;https://www.youtube.com/watch?vZ-65nqxUdl4 努力的小巴掌 记录计算机视觉学习道路上的所思所得。 1、准备图片images 收集数据网站&#xff1a;OPEN IMAGES 2、准备标签labels 网站&#xff1a;CVAT 有点是&#xff1a;支持直接导出yolo格式的标…

搜索引擎的妙用:掌握这些技巧,让你的搜索更高效!

搜索引擎是我们日常生活中不可或缺的工具&#xff0c;它帮助我们快速找到所需的信息。但是&#xff0c;你真的知道如何高效地使用搜索引擎吗&#xff1f;下面&#xff0c;我将分享一些高级搜索技巧&#xff0c;让你的搜索更加精准和高效。 1. 完全匹配搜索 当你想要搜索一个特…

第三十三篇——互联网广告:为什么Google搜索的广告效果好?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 对于信息的利用&#xff0c;再广告这个维度中去洞察&#xff0c;你又能发…

光伏储能为什么变得那么受欢迎?

在当今这个追求可持续发展和清洁能源的时代&#xff0c;光伏储能技术逐渐崭露头角&#xff0c;并成为了能源领域的热门话题。其受欢迎程度不断攀升&#xff0c;背后有着多方面的原因。光伏储能技术的优点众多&#xff0c;涵盖了多个方面&#xff0c;以下是关于其安全、寿命等关…

【BUG记录】条件查询没有查询结果 || MybatisPlus打印查询语句

结论 先说结论&#xff0c;查询没有结果&#xff0c;可能是数据库连接&#xff0c;数据问题之类&#xff0c;最有可能的根本原因是查询语句问题&#xff0c;需要想办法检查查询语句&#xff0c;使用mybatisPlus等自动生成查询语句的框架不能直接看语句&#xff0c;可以依靠日志…

Python低溫半导体电子束量子波算法计算

&#x1f3af;要点 &#x1f3af;任意维度求解器&#xff0c;绘制三维投影结果 | &#x1f3af;解二维静电场、静磁场 | &#x1f3af;狄利克雷、诺依曼条件几何矩阵算子 | &#x1f3af;算法模拟低溫半导体材料 | &#x1f3af;计算曲面达西流 | &#x1f3af;电子结构计算和…

洗地机哪一款好用?家用洗地机推荐

洗地机是家庭必备的高效深度清洁机器&#xff0c;对于家庭用户而言&#xff0c;它可以自动吸尘、拖洗、自清洁&#xff0c;使用便捷高效&#xff0c;为众多家庭用户所欢迎。但市场上的洗地机品牌众多&#xff0c;很多消费者陷入了购买陷阱&#xff0c;为了帮助广大用户选购到合…