嘻嘻,坚持下来了,第六天,第四个游戏也完成了。其中其中真的有好多艰辛,加油。
下面的总结接上19号的博客。之前已经实现了大部分,得分以及游戏结束的UI已经设置了,差的不多了。今天遇到了让我脑袋爆炸的几个问题。首先,先从简单的说起。由于这个游戏是闯关游戏,我们要设置障碍。因为物体的运动是最基本的,我们最先对物体运动以及基本碰撞是没毛病的。在此基础上我们设置关卡。
先是对模型的拖拽,组成一个通道,其中涉及到背景前景以及中景的设置(按先后排序)、一组管道中的两个管道都要挂上Box Collider2D脚本,来对碰撞事件进行发生和接收。
如图,其中C1、C2分别有自己的collider,他们的根节点上也有一个collider,用来判断过关加分,当然关卡是用来通过的,需要勾选触发器(Trigger)。这里一组简单的关卡创建好了,我们需要进行下一步的随机创建。因为前面我们写了GroundScrolling,把这个设置好之后拉到我们Assets中当一个材质包,便于我们的随机创建。因为直接拉入无法实现随机创建效果。这时就要创建一个新脚本ColumnController,也是这个游戏中最新颖的脚本。
public class ColumnController : MonoBehaviour
{
public GameObject columnPrefab;//调用组件用,这个地方就是用来调用我们之前新建的材质的
//内部连接用GetComponent 和FindObjectOfType外部定义加public即可
public int columnMax = 5;//一组最多创建五个,其实这个到不是很影响
public float spawnRate = 3f;//创建新管子的时间间隔
public float yMin = -1f;//Y上限
public float yMax = 3.5f;//Y下限
public float xPos = 10f;//新创建的管子横坐标
float timeSinceLastSpawned;//用来对时间进行累加,判断什么时候应该创建
GameObject[] colums;//接收这一组管子
int currentColumm = 0;//刚开始创建了0个新管子
Vector2 originalPos = new Vector2(-55, -55);//因为我们需要一个二维向量,因此这次一次创建的时候随便在一个我们看不见的地方创建管子就可以了
private void Start()//初始化函数
{
colums = new GameObject[columnMax];//拿到五组新管子当作备用
for (int i = 0; i < columnMax; i++)//从第一组到最后一组新管子循环
{
colums[i] = Instantiate(columnPrefab,originalPos,Quaternion.identity);//Instantiate函数实例化是将original对象的所有子物体和子组件完全复制
}
}
private void Update()//刷新函数
{
timeSinceLastSpawned += Time.deltaTime;//以每一帧为单位进行事件的累计
if (GameMode.instance.gameOver==false&&timeSinceLastSpawned>=spawnRate)//游戏还没有结束且此帧与上一组管子被创建的时间差值已经超过了规定创建时间
{
timeSinceLastSpawned = 0f;//初始化计数时间
float yPos = Random.Range(yMin, yMax);//y轴位置随机创建
colums[currentColumm].transform.position = new Vector2(xPos, yPos);//x位置确定,y位置随机给出,让本对管子的transform位置等于新位置
currentColumm++;//遍历组数加一
if (currentColumm>=columnMax)//要是越界
{
currentColumm = 0;//组数初始化
}
}
}
}
由于本次的方法比较特殊,这里我对其进行一些总结和概述,以加深印象。
由于单个组件直接在Hierarchy中操作,很难进行无数次复制和随机创建,因此我们要在后台完成一系列操作,脚本的逻辑性更强。由于是脚本对于我们新材质的直接调用,因此要设置一个接收外部材质的接收器:public GameObject columnPrefab;
之后就 对必要的变量进行设置。这里的循环利用数组进行循环,而不是直接创建无数组组件,这样太过于占用内存,且我们的游戏画面也不大,因此五个足够,速度如果设置的足够,可能五个都用不到。这里的五个指的是一个数组里面装着五组材质,数组的最大数量也要用变量表示:public int columnMax = 5;
和这个联系非常紧密的还有每一组新管子出现的时间间隔,这里设置成3,不快不慢。上面之所以说五个足够,因为五组管子需要每个走三秒,这样就是十五秒,我们只需保证十五秒内走完一个场景就可以了。要是无限创建组件而不进行free会占用很多内存,这里我们直接用五个作为循环。我们随机创建的时候x轴可以在固定位置出现,随后和BackGround一起滚动,可是y轴上的关卡一定要保证其多样性,因此每组管子的高低有一个波动范围,设置最高最低值,调用random.range方法对每组管子的y轴波动范围进行限制。已知每三秒更新一次,那就要有一个计时器,这里取一个float变量来计时,计时方式就是对每一帧的时间进行相加,超过后初始化并进行新管子组的创建。
由于我们之后的每一组管子都是复制于第一组的 ,我们要先创建一个第一组管子以供后面的兄弟进行复制。都存在一个看不见的位置,以供后面的取到。也就是说先复制五组管子在一个看不见的位置,然后再到该复制第几个管子的时候,复制第几个管子,说实话有点浪费,不过架不住现在是照葫芦画瓢。这里有一个大神写道对与Instantiate方法的理解和解释,这里附上网站:
https://blog.csdn.net/qq_29413829/article/details/78934579
至于在更新方法中,最先就是对时间进行累加,来判断是否该进行新组管的放置。而随后就是是否该将其归零。也就是说要同时满足游戏进行(没死亡)和计时器已经在本帧超过了规定创建时间间隔。归零的同时肯定也伴随着其新管子组的创建和对这一组管子是否用完,该不该进货新管子的判断:
timeSinceLastSpawned = 0f;
float yPos = Random.Range(yMin, yMax);//roll点roll一个位置
colums[currentColumm].transform.position = new Vector2(xPos, yPos);//对这一组管子进行移动(原来位置在我们看不见的位置)
currentColumm++;//用完一组
if (currentColumm>=columnMax)//数组越界,新进货管子
{
currentColumm = 0;
}
那么,这个狗血的脚本就结束了。我们要进行最后的工作。先把Column挂在GameMode上,再创建一个新脚本。
利用触发器来进行加分(这个要加在我们管子组材质的根节点上,因为得分boxCollider就在一组管子的两个管子之间也挂在我们的根节点上。跟随之前UI的脚步。我们取得了胜利。
注意点:
1、在创建场景的时候要将场景的transform.position 初始化
2、在创建2D作品的时候不要将多余的轴设置多余的数值,不然会出现一些问题,平面看起来没事,但是运行程序后就出现的问题。
比如我们在2D中看到正常的平面:
在3D中可能就是这个样子:
我们很有可能因为这一个小小的错误在camera播放的时候game中视角出现错误,让游戏出现意料之外的bug。
好的,之后的总结中可能还会更新。这个游戏先告一段落。
晚上快结束了,我进行几点简单的总结,因为第四个遇到了不少问题,之前的基础也没掌握牢固,我就觉得在第四个游戏的基础上进行修改。无奈自己的基础不够牢固,在修改的过程中虽然解决了一些问题也遇到了更大的问题。
第四个游戏是flappybird,是一款折磨人的游戏,我想把它改成一款暴力得分游戏。首先我从死亡函数下手,将有关死亡的方法全部注释,其次,极大的缩小了管子的刷新频率,极大的扩大了每一数组内可装组数大小。把所有的管子组的每一个部分的BoxCollider全部勾上trigger让小鸟及其安全。可为了让玩家的心情更加愉悦,我想把小鸟进行强壮化,即每穿过一个管子组,撞到的那一块直接消失且播放爆炸音效及animator。这里出现了一些问题。在PlayerCharacter脚本中,最后有一个利用触发器断定小鸟是否撞上其他boxcollider的方法。为了让小鸟不减速,我将触发器方法变成了OnTriggerEnter而不是OnCollisionEnter,为了爆炸特效,在脚本中public 了一个ParticleSystem,并在触发碰撞函数后play,当然还有audioclip。可是这些都缺少了一些东西。
今天需要思考的重点是:如何利用ParticleSystem、如何用OnCollisionEnter和OnTriggerEnter方法、如何给移动的物体加特效