ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Unity初级教程贪吃蛇实现(Snake)

2021-06-06 16:30:11  阅读:205  来源: 互联网

标签:蛇头 位置 transform Unity 贪吃蛇 初级教程 tpos new 个蛇身


学习Unity后做游戏

在此先声明,本素材是由siki学院处取得的,无商业用途,仅学习使用。
       这次,默认全屏1920*1080;而且蛇头蛇身连接处处理的不恰当,开局默认两个蛇身且没有碰撞体,所以就是开局除了撞墙不会死亡…吃过一个食物才会撞自己死亡;快速按键可以回头撞自己…缓慢的话就不会了,这些BUG我慢慢解决,这回先这样了,动手能力强的也可以解决下,那就废话不多说,开始教程了。
        首先有两个场景,一个开始界面,一个游戏场景。开始界面有一个开始游戏Button,两个Toggle Group分别存放模式和皮肤,两个Text存放最高分和最大长度,其他的背景什么的随便丢丢上去就好了(注定不会做UI的我)皮肤先开始用Sprite Render展示的,突然发现设置Pivot不管用,于是先设置了Order in Layer的顺序来展示了…但是后面找到设置Pivot为啥没用了,在Edit=>Project Settings=>Graphics=>Camera Settings=>排序模式设置为透视就好了,设置为自定义的话x,y,z没法设置,感觉就只能设置成这样了,哦对了Toggle设置单选需要在两个Toggle上面设置空的父物体,然后加个组件叫Toggle Group就行了,然后把两个Toggle的Group设置为父物体就能实现单选了。开始游戏按钮需要绑定一个事件,手动绑自动绑都行,手动绑就加个函数绑就行了;自动的话onclick事件上面添加委托,直接lambda表达式就搞定了,就像这样:button.onClick.AddListener(() => SceneManager.LoadScene(1));其他玩家选项和分数的控制的储存一会儿一起说。

丑不拉几之丑不拉几

然后游戏场景,开始了漫长的数格子过程…我这里决定的是食物和蛇都是占两个格子的,但是都可以一格一格的挪动,所以先把蛇头设置为2*2格开始找四个角的边界记录下来,然后移动两格开始找两步的长度。开始我设置的边界离真实的边界有一格距离,步长为两格,后来发现无尽模式没办法好好的传送到另一边,所以又改了一格一格的挪,边界也改成现在这样了。开始的时候找步长还找了大半天…找了最小公倍数,还一格一格算了有多少格,结果发现走弯路了,根本就不用找…按照我上面说的方法一下就完事了。
        记录完以后,就可以自己写个移动方法来测试了,先写个全局变量moveVec2储存每次按键过后的方向,timer计时,然后计时结束以后移动长度为一格步长。

if (h != 0 && moveVec2.x != -h)//水平轴有偏移且正在移动的方向与偏移方向不相同,就是禁止180°转头   一涉及到逻辑就开始歇菜了o_o智商明显不在线
{
    moveVec2.x = h;//更换移动方向
    moveVec2.y = 0;
    if (h > 0)//设置旋转角实现转头
        transform.eulerAngles = new Vector3(0, 0, -90);
    else
        transform.eulerAngles = new Vector3(0, 0, 90);
}
else if (v != 0 && moveVec2.y != -v)//和上面一样的,不写注释了
{
    moveVec2.y = v;
    moveVec2.x = 0;
    if (v > 0)
        transform.eulerAngles = new Vector3(0, 0, 0);
    else
        transform.eulerAngles = new Vector3(0, 0, 180);
}

下面就是更重要的逻辑了——移动蛇身,自己脑子里想不动笔真的是很难一下子想出来,想了好久后画了张图一下子思路就清晰了,这张我画的…

在这里插入图片描述

咳咳,讲解一下,首先蛇头是i-1,也就是第0位,后面都是蛇身。蛇头动与身体无关,所以蛇头和身体的连接那一段单独处理。首先记录蛇头位置tpos,第i个蛇身位置pos,第i个蛇身移动到tpos位置,然后pos的值储存在tpos里,再把第i+1个蛇身原本位置储存pos,第i+1个蛇身位置变换为tpos,以此类推。总之tpos是要变换的位置,pos储存之前的位置。(啊喂命名的真随便)代码的话就是这样子的。

if (timer < 0)//到时间了该移动了    难点来了,蛇身跟着蛇头移动,实现方法为后面的跟着前面的移动,蛇头蛇身连接部分单独处理
{
    for (int i = 1; i < snakeList.Count; i++)//从第一个蛇身开始遍历
    {
        Vector3 pos = snakeList[i].transform.position;//存储第i个蛇身位置
        if (i == 1)//第一个蛇身单独处理
        {
            tempPos = snakeList[i - 1].transform.position;//tpos储存蛇头位置   tpos=tempPos,我简写了
            snakeList[i].transform.position = tempPos;//蛇身移动到蛇头位置
            tempPos = pos;//tpos储存的位置变为第1个蛇身的位置
            continue;
        }
        snakeList[i].transform.position = tempPos;//第i个蛇身的位置变为tpos的位置,即第2个蛇身变为第1个蛇身的位置
        tempPos = pos;//tpos储存的位置变为第i个蛇身的位置
    }
    transform.position += new Vector3(moveVec2.x * runSpeed * footStep / 2, moveVec2.y * runSpeed * footStep / 2);//移动蛇头
    timer = 0.3f;//重新计时
}//说难也不难,最难的就是从无到有的过程,需要一遍一遍的自己构思

除了这两个难点以后再就是食物的生成位置判断了,开始只写了一个检测有莫名其妙的bug,后面干脆多写了个射线的检测,这部分我写的逻辑比较乱,没怎么大幅修改,所以这部分仅供参考吧,至少没问题了,有时间我再修改修改。代码就直接贴出来了,注释写的很全就不再解释了。

public void OnEnable()
{
    short y;//食物x位置
    short x;//食物y位置
    while (true)
    {
        x = (short)Random.Range(0, Mathf.Round((bound[3] - bound[2]) / footStep * 2) + 1);//随机生成x,y位置
        y = (short)Random.Range(0, Mathf.Round((bound[0] - bound[1]) / footStep * 2) + 1);
        if (!Equals(new Vector2(Mathf.Round(bound[2] + x * footStep), Mathf.Round(bound[1] + y * footStep)), new Vector2(Mathf.Round(SnakeHead.transform.position.x), Mathf.Round(SnakeHead.transform.position.y))))//生成的位置和蛇头的位置不同时跳出循环
            break;
    }
    RaycastHit2D ray = Physics2D.BoxCast(new Vector2(bound[2] + x * footStep / 2, bound[1] + y * footStep / 2), new Vector2(transform.localScale.x, transform.localScale.y), 0, new Vector2(0, 0), LayerMask.GetMask("PlayLayer"));//创建方块射线检测该区域是否有别的物体
    if (ray.collider == null)//检测到没有东西
    {
        GetComponent<SpriteRenderer>().sprite = foodSprite[Random.Range(0, foodSprite.Length)];//随机替换食物图片
        transform.position = new Vector3(bound[2] + x * footStep / 2, bound[1] + y * footStep / 2);//生成新位置
        GetComponent<SpriteRenderer>().enabled = true;//图片可见
    }
}
private void OnTriggerStay2D(Collider2D collision)
{
    if (collision.gameObject.layer == LayerMask.NameToLayer("PlayLayer") && !collision.CompareTag("SnakeHead"))//碰到playlayer中不是蛇头的物体的话重新生成食物
    {
        GetComponent<SpriteRenderer>().enabled = false;//食物图片不可见
        OnEnable();
    }
}

到这里其他的代码和操作都没有什么需要注意的东西了,哦还有,素材里面还有一个特殊道具,这个我没有实现,有想做的小伙伴可以尝试一下,做长度加二或者加得分都是可以的,本次用到了数据存储相关的东西,其实unity已经定义好了一个类PlayerPrefs用于储存一些小型数据,要是大型的话还是老老实实的用文件储存吧,这个太耗费性能了,但是优点就是读取较快,直接就可以从内存中读取出来,一般用到的就是里面的get,set方法,不过没有bool类型,需要string或者int类型存储,建议使用int,string的话因为是引用,频繁修改需要频繁在堆上开辟新空间,不断的使用GC回收,很耗费性能,其他没什么注意的地方了。
       越写才越觉得自己水平低,也可能看别人的代码少,遇事不决就if…到了大型项目的时候这条路肯定是走不通的,大家不要学我,推荐大家写代码入门到一定程度以后把《大话数据结构》看一遍,不研究算法那些东西但是数据结构只要是写代码就必须要懂的,学过数据结构大学才不“白上”,不然以至于进了公司都要被项目经理嘲笑。好了本次就这么多,具体的东西看我的代码和设置吧,直接贴链接了。
       
       
       
链接:https://pan.baidu.com/s/159vq3liZU6Kkztpz_We5wQ
提取码:gvmk

标签:蛇头,位置,transform,Unity,贪吃蛇,初级教程,tpos,new,个蛇身
来源: https://blog.csdn.net/xiheng66/article/details/117628659

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有