文章目录

  • 前言
  • 敌人1脚本
  • 预制件
  • 敌人1的完善工作
    • 触发器式碰撞
    • 屏幕边缘检测
    • 计算敌人1的飞行角度
    • 优化敌人生成器

前言

[Unity]《太空射击》开发日记Ep.1(入门篇)
在上一篇日记中,我从最最基本的一些操作开始,抛弃了美工、架构等等待优化的方面,很直接的实现了一个键盘控制主角的效果。这篇日记会在上篇的基础上更加深入一些,要把基础用的更熟练一点。
PS:下面出现的所有动图都是偏快的!

敌人1脚本

Enemy1我想让他有自己的意识——朝一个随机方向移动
有了上次的脚本作为参照,这个功能看起来就没那么复杂了。不过一切还是要按部就班的来:
新建脚本enemy1MoveController,添加类成员,实现Update()函数。

public class enemy1MoveController : MonoBehaviour
{public float moveSpeed;private float xAngle;private float yAngle;// Use this for initializationvoid Start(){//随机获取弧度float rad = Random.Range(0.0f, 2 * Mathf.PI);xAngle = 1 * Mathf.Cos(rad);yAngle = 1 * Mathf.Sin(rad);}// Update is called once per framevoid Update(){transform.Translate(new Vector2(xAngle, yAngle) * moveSpeed * Time.deltaTime);}
}

代码说明:
1.只有public的类成员才会在Unity中作为一个属性显示出文本框供赋值。
2.Start()是附着的对象生成时执行一次的函数,一般用来初始化。
3.Random.Range(float min, float max):返回一个[min,max]的随机数,因为传入了float,所以返回的也是float。
4.底下的Vector2(x,y)实际上是一个表示方向的单位向量(cosθ,sinθ),相当于0~360°随机方向。
但如果Enemy1在屏幕边缘生成的话,随机到往屏幕外的方向怎么办
事实上我们想要的理想结果是,Enemy1随机地从某个边缘进入屏幕,然后朝对面直线飞行。
所以我们还需初始化Enemy1的位置,并且生成对应的合理的随机角度。

 public float moveSpeed;private float xAngle;private float yAngle;private float xPos;private float yPos;// Use this for initializationvoid Start(){float rad=0;//{0,1,2,3}={右边缘,上边缘,左边缘,下边缘}int direction = Random.Range(0, 4);switch (direction){case 0://90~270°rad = Random.Range(0.5f * Mathf.PI, 1.5f * Mathf.PI);xPos = 5;yPos = Random.Range(-5.0f, 5.0f);break;case 1://180~360°rad = Random.Range(1.0f * Mathf.PI, 2f * Mathf.PI);xPos = Random.Range(-5.0f, 5.0f);yPos = 5;break;case 2://270~450°rad = Random.Range(1.5f * Mathf.PI, 2.5f * Mathf.PI);xPos = -5;yPos = Random.Range(-5.0f, 5.0f);break;case 3://0~180°rad = Random.Range(0.0f, Mathf.PI);xPos = Random.Range(-5.0f, 5.0f);yPos = -5;break;}xAngle = 1 * Mathf.Cos(rad);yAngle = 1 * Mathf.Sin(rad);//位置初始化transform.position = new Vector2(xPos, yPos);}

虽然这样子无法避免Enemy1朝侧边飞行的情况,但起码Enemy1一定是朝屏幕内飞行的。以后可以再对Enemy1进行完善。

预制件

只有一个Enemy1没什么难度,一定要弄很多个一起攻击主角才有趣
Unity中可以把一个精灵当作一个文件来保存,称为预制件(Prefab),相当于一个克隆体。并且只要母体改变,子克隆体也会随之改变,很适合长期保存修改和动态生成游戏对象。
预制件的文件后缀为.prefab,我们同样用一个Prefabs文件夹来管理所有的预制件
,并且为了后续的开发,我们要调整一下项目文件夹结构:先在根目录创建一个Resources文件夹,再在里面自定义我们自己的文件夹。新的文件目录就长这样

剩下的就很简单,直接把Player和Enemy1拖到Prefabs中,他们会自动变成预制件。
如何用预制件来生成一堆Enemy1呢
我们可以利用场景里必存在的Camera做个小试验,给他附加一个生成Enemy1克隆的脚本。
新的脚本叫做enemy1BornController,添加到摄像机(Main Camera)上,代码简单的出乎意料:

public class enemy1BornController : MonoBehaviour {public GameObject pre_Enemy1;// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {Instantiate(pre_Enemy1);}
}

代码说明:
1.公共的GameObject同样可以在Unity中进行赋值,就用刚做好的Enemy1.prefab。
运行后大概是这个效果。

敌人1的完善工作

上面的小试验可以看出Enemy1还存在诸多问题。
1.在与Player碰撞后,应该销毁Player和自身(暂定只有一条生命)。
2.克隆体对象飞出屏幕后一直存在于游戏中,引起资源浪费。
3.(之前待解决的)飞行轨道凌乱不堪,应朝对边飞行而不是左右边。
4.在克隆体实验成功后,思考如何生成一群合理的敌人。
这些问题或早或晚都是要解决的。

触发器式碰撞

主角碰到Enemy1之后,应该要受到伤害
这个效果的核心在于检测"碰撞",这需要我们给Player和Enemy1定义有效碰撞区域。当两个区域重叠后,就视为发生碰撞。
第一步,给两个预制件添加碰撞区域。
在Inspector窗口中,选择Add Component>Physics 2D>Box Collider 2D。因为我们实际上不需要Enemy1间的互相碰撞,所以把Enemy1的Box Collier的is Trigger勾选上,不然会出现Enemy1之间互相排挤的现象。
第二步,给两个预制件添加刚体组件。
在Inspector窗口中,选择Add Component>Physics 2D>Rigidbody 2D。并且因为我们是在太空,所以是不受重力影响的,因此要把Linear Drag, Angular Drag, Gravity Scale都赋为0。

第三步,将Player的Tag属性设置为Player,将Enemy1的Tag属性设置为Enemy。
这个也在Inspector窗口的最顶上,是一个下拉菜单框的样子。
第四步,编写并添加碰撞脚本。
新脚本名为playerTrigger,添加到Player上。

public class playerTrigger : MonoBehaviour {// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {}private void OnTriggerEnter2D(Collider2D other){if(other.gameObject.tag=="Enemy"){Destroy(other.gameObject);Destroy(gameObject);}}
}

代码说明:
1.OnTriggerEnter2D表示该gameObject的碰撞体(Collider)碰到了other.gameObject的触发器(Collider & is Trigger==True),在此例中就是Player的BoxCollier碰到了Enemy1的Trigger BoxCollider。
2.判定tag属性是一种方式,也可以直接判定name属性。
3.Destroy是销毁游戏实例,注意要先销毁对方,再销毁自己。
运行游戏,现在Player碰到Enemy1后两者都会被销毁。

屏幕边缘检测

Enemy1飞出屏幕之后还会一直存在,如何及时销毁啊
这时候就有个很巧妙的办法,利用我们刚刚掌握的碰撞机制,在屏幕外围上四堵空气墙(不要盖到Enemy的生成区),然后只要有Enemy碰到空气墙,就会被销毁。
为了追求精简,我们先在场景中新建一个左边的空气墙。在Hierachy窗口中,右键Create Empty
然后给这个对象重命名EdgeBox并Reset坐标。
然后先添加左侧的Box Collider 2D,我打算给左侧预留1个unit的空间,勾选好is Trigger并设置参数Offset(-6.5,0),Size(1,12)。Offset指的是碰撞体距离对象位置的偏移量,Size指的是碰撞体的方块尺寸。
仿照这个参数,我们再添加3堵另外3个方向的空气墙(Box Collider 2D),也就是说可以用多个碰撞体叠加成一个更大的碰撞体。
最后添加一个静态的的Rigibody 2D即可。(Body Type为Static,意思是一动不动)
效果如下图:

有了碰撞体之后,最后只要添加一个销毁脚本就行了。
enemy1MoveController里添加碰撞函数:

private void OnTriggerEnter2D(Collider2D other){if(other.name=="EdgeBox"){Destroy(gameObject);}}

为了看到效果,我们可以把Main Camera的Size设置为7。

这么说来,主角也可能跑到屏幕外去,顺便也做个挡住玩家的空气墙吧
再次新建一个Empty GameObject叫做BoundaryBox,很多设置都与上面类似,这里我就贴一部分吧。另外如果只是墙的作用的话,就不用额外添加脚本了,Unity自带刚体间的互斥作用。

计算敌人1的飞行角度

要让敌人1的飞入边和飞出边是对边,这样才有足够的飞行时间

之前我们的思路是,随机生成飞入点,然后再随机相应飞行角度。现在既然要限定飞出点的范围,那么就改为随机飞入点和飞出点,两点也能确定一条直线。
另外这里也做一个小优化,现在点的坐标都在10x10的周长上,但这样其实是不自然的,Enemy1是不完全在屏幕外生成的,导致有种突然出现的感觉(虽然不明显)。因此我们稍微拉远一点,在10.5x10.5的边缘生成。
enemy1MoveController修改:

public float moveSpeed;private float xPos;private float yPos;private float wPos;private float vPos;// Use this for initializationvoid Start(){//{0,1,2,3}={右边缘,上边缘,左边缘,下边缘}int direction = Random.Range(0, 4);switch (direction){case 0:xPos = 5.5f;yPos = Random.Range(-5.0f, 5.0f);wPos = -5.5f;vPos = Random.Range(-5.0f, 5.0f);break;case 1:xPos = Random.Range(-5.0f, 5.0f);yPos = 5.5f;wPos = Random.Range(-5.0f, 5.0f);vPos = -5.5f;break;case 2:xPos = -5.5f;yPos = Random.Range(-5.0f, 5.0f);wPos = 5.5f;vPos = Random.Range(-5.0f, 5.0f);break;case 3:xPos = Random.Range(-5.0f, 5.0f);yPos = -5.5f;wPos = Random.Range(-5.0f, 5.0f);vPos = 5.5f;break;}//位置初始化transform.position = new Vector2(xPos, yPos);}// Update is called once per framevoid Update(){Vector2 dir = new Vector2(wPos - xPos, vPos - yPos).normalized;transform.Translate(dir * moveSpeed * Time.deltaTime);}

代码说明:
1.vector2.normalized是将Vector2的一个实例转换为同方向的单位向量,我这么做的目的就是让向量单纯表示方向,moveSpeed就能统一所有Enemy1的速度。
2.飞入点为(x,y),飞出点为(w,v)。

优化敌人生成器

现在这游戏还不能玩的原因是敌人太多了,我们要让生成间隔减小一点。
目前Enemy1是1帧生成一个,不如把这个1变为一个公开变量,这样可以自己不断调试来确定一个合理的值。
enemy1BornController修改:

public class enemy1BornController : MonoBehaviour {public GameObject pre_Enemy1;public float bornColdDown;//Enemy1的生成间隔private float pasttimeSingle;//单个Enemy1生成后已过去的时间// Use this for initializationvoid Start (){}// Update is called once per framevoid Update () {pasttimeSingle += Time.deltaTime;//达到冷却时间if(pasttimeSingle>= bornColdDown){pasttimeSingle -= bornColdDown;Instantiate(pre_Enemy1);}}
}

代码说明:
1.相当于每一帧都累加一次时间数,达到所需的冷却时间就生成一个Enemy1并刷新时间数。

回到Unity里,这个脚本是在Main Camera上面的,这时候就多了一个Born Cold Down供我们填写,我们填入0.25,也就是1秒4个的生成速度。

这样,一个非常非常非常简陋的小游戏就算“小”功告成了!

[Unity]《太空射击》开发日记Ep.2(初级篇)相关推荐

  1. unity太空射击源码_引擎入门 | 创建双杆射击游戏(入门 2)

    点击上方"蓝字"关注我们吧! 本期我们继续为大家进行双杆射击游戏的后续教程 学习内容 3.整理资产 4.导入资产 5.安装 Blender 6.导入包 文章末尾可免费获取教程源代码 ...

  2. [Unity]《太空射击》开发日记Ep.1(入门篇)

    文章目录 前言 新建 素材 精灵 脚本 组件 前言 这次呢,又接到一个"个人开发一个独立项目"的大作业.我毅然决然选择了做一个休闲小游戏(毕竟这是我的爱好吧),时间大概是3周左右. ...

  3. 对 Unity 太空射击游戏的实践

    写在前面 # 本次 Space Shooter 实践通过实现以下功能达到加深对 U3D 游戏开发的认知. 键盘控制飞船移动; 发射子弹设计目标; 随机生成大量障碍物; 计分; 实现游戏对象的生命周期管 ...

  4. UNITY OCULUS QUEST开发入门教程 1 - 准备篇

    随着OCULUS QUEST2在市场获得的现像级大反响,VR正逐步从硬核玩家走向普通大众. 作为UNITY开发者的你,是否对OCULUS QUEST VR开发也感兴趣呢,那么不仿一起来跟着我们的教程来 ...

  5. 【Android的从零单排开发日记】之入门篇(十六)——Android的动画效果

    什么是动画,动画的本质是通过连续不断地显示若干图像来产生"动"起来的效果.比如说一个移动的动画,就是在一定的时间段内,以恰当的速率(起码要12帧/秒以上,才会让人产生动起来的错觉) ...

  6. 开发日记-20190513 关键词 汇编语言(六)

    其实,每次当我写一篇开发日记,并且这篇开发日记并没有关键词,或者我并没有和你扯东扯西的情况下... 这意味着我这天偷懒了= = ;AddTwo.asm --两个32位整数相加.386.model fl ...

  7. Unity空间射击游戏开发教程

    描述 在本课程中,您将学习如何在unity中制作一款太空射击游戏.本课程使用全新的特性和编码实践,并且兼容所有较新版本的unity. 了解如何使用世界领先的免费游戏开发工具Unity创建太空射击游戏. ...

  8. unity太空飞机射击游戏

    unity太空飞机射击游戏 有分数和生命值,飞机可以对其他飞行物进行射击,分数提高后飞机可以升级,具体情况如下图: 点我下载资源

  9. unity开发日记之火箭发射

    2020-06-25 夜晚,思考人生的意义,乔布斯在斯坦福的演讲说到,你的热爱就是你的事业,你的事业就是你的热爱,我想了想,什么是自己的热爱呢,这么多年,陪伴在自己身边的也就是充满乐趣的游戏吧.自己不 ...

最新文章

  1. KeUserModeCallback用法详解
  2. java中交通灯管理系统_java案例--交通灯管理系统学习
  3. 微课|中学生可以这样学Python(2.3.3节):map()函数
  4. 数 AI 人物还看今朝!CCAI 2017 人工智能青年论坛即将启航
  5. python自定义函数两个返回值如何分别输出_第八讲 python自定义函数返回值
  6. XML语言学习2---方立勋教程DTD
  7. python查找在圆周率100万个数字中是否有你的生日
  8. 【博客目录】成为一个优秀的数据工程师
  9. Win10电脑关机后立即自动重启怎么办
  10. 百度冰桶算法说明,如何避免冰桶算法呢?
  11. VC++公安指纹识别系统
  12. 十二黄金圣斗士阴险程度(爆笑)
  13. 文献阅读_Radiogenomic analysis of vascular endothelial growth factor in patients with diffuse gliomas
  14. Java基础03:数据类型讲解
  15. 2022年秋招 Java后端程序员如何应对面试?
  16. html 语言注释,HTML 注释
  17. 【SemiDrive源码分析】【MailBox核间通信】43 - 基于Mailbox IPCC RPC 实现核间通信(代码实现篇)
  18. CTS/RTS/DSR/DTR
  19. 如何让人形机器人“行稳致远”?这篇顶级期刊的论文提出了新方法
  20. open-dingtalk和nginx 做内网转发

热门文章

  1. “高质量”的手机号,让电销更高效
  2. 文献阅读利器--HistCite安装与使用
  3. 数学建模笔记-熵权法确定评价类问题指标的权重 清风课程笔记整理
  4. 国内模具及注塑行业(精辟模具文章)
  5. 【财经期刊FM-Radio|2020年11月25日】
  6. Jetson nano部署Yolov5 ——从烧录到运行 1:1复刻全过程
  7. ORACLE取当前日期上一个季度的开始日期和结束日期
  8. cloudreve-自建云盘
  9. 统一门户的几种建设方案(备忘录)
  10. 设计微信公众号菜单栏的几点建议