Unity3D初级案例-经典贪吃蛇一

引言:小生今日分享的是经典贪吃蛇案例,特别感谢Siki学院的老师们。
这里附上原视频链接: http://www.sikiedu.com/my/course/89 可以搭配起来学习哦!
小生会根据自己理解,做一些代码上的修改!大家也可以有自己的主见!
开发版本:unity 2017.1.1f1
适合人群:初学Unity者
源文件链接请见文末!
开启学习之旅吧!

效果预览:
主要实现功能:WASD键或上下左右键控制蛇移动方向,吃到冰淇淋加分,并且增长蛇身。游戏提供两种蛇的样式可选,而且有两种有无边界模式可选。记录当前得分和历史最高分。

01 场景搭建

新建2D工程,新建StartScene场景,Game场景设置为1280*720大小,导入资源
我们使用UGUI制作UI及人物,开始界面效果如下:
Canvas要设置为Screen Sapce Camera模式
需要注意的是,皮肤和模式分别只能二选一,Toggle Group的Allow Switch off不能勾选
新建主场景MainScene,效果如下:
因为需要开发一个边界模式,新建四个对象,放入场景四边,并添加碰撞体,勾选Is Trigger
注意四面墙分别命名为Up,Down,Left,Right,新建一个父对象统一管理

02 实现思路

有两种主要方式可以实现蛇的移动
第一种方式,从蛇尾的最后开始一节开始,依次向前一节蛇身的位置移动
第二种,蛇头每向前移动一个位置,就将蛇尾的最后一节移动到蛇头刚才的位置
我们选择第一种方式,因为我们的蛇有两种颜色相间的蛇身,如果第二种方式,会让蛇身的颜色混乱

03 开发蛇头

新建一个Image,命名为SnackHead,Source Image修改为蛇头图片,长宽设置为45*45,添加Rigidbody2D和BoxCollider2D组件,勾选Is Trigger,为其添加一个空的父物体Snack,方便管理蛇头和以后添加的蛇身
在SnakeHead上挂载脚本SnakeHead.cs
首先实现蛇头的移动,转头,空格键加速操作
实现主要思想,InvokeRepeating方法持续间隔一定时间调用控制蛇头移动的Move方法,然后Update方法监听按键,判断蛇头移动的XY增量
并实现蛇头转向,利用InvokeRepeating的间隔调用时间来控制蛇头移动的速度
需要注意根据实际情况设计蛇头移动步长Step的大小,最好能让蛇头在有限范围内移动整数步

 public float velocity = 0.35f;
    //每一步蛇头移动距离
    public int step;
    //x轴蛇头移动增量
    private int x;
    //y轴蛇头移动增量
    private int y;

  private void Start()
    {
        //初始化,让蛇头可以向上移动
        x = 0;
        y = step;
        //InvokeRepeating等待0秒,然后每隔velocity时间调用Move方法
        InvokeRepeating("Move", 0, velocity);
    }

  private void Update()
    {
        //虚拟轴控制移动
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        //Input.GetKeyDown键按下瞬间
        if (Input.GetKeyDown(KeyCode.Space))
        {
            //CancelInvoke先取消之前的InvokeRepeating命令
            CancelInvoke();
            //将间隔调用“Move”方法的时间减小,则蛇移动变快
            InvokeRepeating("Move", 0, velocity - 0.2f);
        }
        //Input.GetKeyUp键抬起瞬间
        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
        //如果此时y = -step说明蛇正在向下移动,为了防止蛇目前在向下移动,突然向上移动,加y != -step判断,以下同理
        {
            //设置当头上下左右移动的时候,蛇头的方向和移动方向一致,以下同理
            //Quaternion代表四元数,identity表示初始旋转角度,可理解为new Vector(0,0,0)
            gameObject.transform.localRotation = Quaternion.identity;
            //设置蛇的移动方向,x = 0,y = step说明蛇头在Y轴向上移动,以下同理
            x = 0;
            y = step;
        }
        if (v < 0 && y != step)
        {
            //Quaternion.Euler将欧拉角转化为四元数,需要注意欧拉角要与移动方向匹配
            gameObject.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 180));
            x = 0;
            y = -step;
        }
        if (h < 0 && x != step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 90));
            x = -step;
            y = 0;
        }
        if (h > 0 && x != -step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, -90));
            x = step;
            y = 0;
        }
    }

    void Move()
    {
        //获取当前蛇头移动的局部坐标
        headPos = gameObject.transform.localPosition;
        //将蛇头当前的移动位置加上x轴和y轴的移动增量,实现蛇头的移动
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, 0);
    }



04 食物生成

开发食物,新建一个Image,命名为SnackBody,source image设置为任意的食物图片,长宽设置为35*35,添加Box Collider2D,勾选is trigger, 需要注意碰撞器大小设置稍微小一点,防止蛇头擦肩而过的时候,发生碰撞,标签设置为Food,最后设为预制体。
设置步长step为30px,计算后得到世界坐标中心点到四周的步数,但为了防止出现食物"卡"在边缘,如下图,将步数各减一步
在Canvas下设置一个子物体FoodRoot,挂载脚本FoodCreator.cs
将FoodCreator设置为单例模式,只需实例化一次,方便调用
我们规定游戏开始,就生成一个食物,蛇每吃掉一个食物就随机生成其他食物,并有一定几率生成特殊奖励
特殊奖励的预制体和食物制作差不多。

public class FoodCreator : MonoBehaviour
{
    private static FoodCreator instance;
    public static FoodCreator Instance
    {
        get
        {
            return instance;
        }
    }
    public int xMinLimit = 11;
    public int xMaxLimit = 20;
    public int yMinLimit = 11;
    public int yMaxLimit = 11;
    //要和蛇头移动步长一致
    public int step = 30;
    public GameObject foodPrefabs;
    //设置
    public GameObject rewardPrefabs;
    //保存食物的Sprites
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    private void Start()
    {
        foodHolder = GameObject.FindGameObjectWithTag("FoodRoot").transform;
        //游戏开始时,就生成一个食物
        CreateFood(false);
    }

    public void CreateFood(bool isReward)
    {
        //随机取得一个食物的下标
        int index = Random.Range(0, foodSprites.Length);
        //实例化food预制体
        GameObject food = Instantiate(foodPrefabs);
        //将食物的image source改为选中下标的食物
        food.GetComponent<Image>().sprite = foodSprites[index];
        //将food设置为foodHolder的子物体,false会使得food保持局部坐标不变
        food.transform.SetParent(foodHolder, false);
        //随机取得食物生成位置
        int x = Random.Range(-xMinLimit, xMaxLimit + 1);
        int y = Random.Range(-yMinLimit, yMaxLimit + 1);
        food.transform.localPosition = new Vector3(x * step, y * step, 0);
        //判断是否生成奖励
        if (isReward)
        {
            //同理
            GameObject reward = Instantiate(rewardPrefabs);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xMinLimit, xMaxLimit);
            y = Random.Range(-yMinLimit, yMaxLimit);
            reward.transform.localPosition = new Vector3(x * step, y * step, 0);
        }
    }
}



05 处理蛇身的生成

蛇身SnakeBody的制作和前面类似,tag设置为Body,设为预制体
我们使用List<Transform>来存储蛇身,需要注意引用命名空间using System.Collections.Generic;
在SnakeHead.cs中新增AddBody()方法,然后添加OnTriggerEnter2D()用于触发检测,当碰到食物或者奖励的时候,调用AddBody()方法,实现蛇吃食物增加蛇身的功能
  void AddBody()
    {
        //三元运算符,如果bodyList.Count被2模除则返回0,否则返回1,控制身体奇偶数轮换颜色
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        //new Vector3(2000, 2000, 0)先将身体实例化在屏幕外
        GameObject newBody = Instantiate(bodyPrefab, new Vector3(2000, 2000, 0), Quaternion.identity);
        newBody.GetComponent<Image>().sprite = bodySprites[index];
        newBody.transform.SetParent(snackRoot, false);
        //将新生成的蛇身加入到bodyList中
        bodyList.Add(newBody.transform);
    }

 private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            AddBody();
            //(Random.Range(0, 100) < 20) ? true : false 三元运算符 随机值小于20则返回true,否则false
            FoodCreator.Instance.CreateFood((Random.Range(0, 100) < 20) ? true : false);
        }
        else if (collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            AddBody();
        }
}



06 实现蛇移动

实现方法如图所示
在SnakeHead类中添加Move方法,使用 List<Transform>记录蛇尾的位置信息
public List<Transform> bodyList = new List<Transform>();
 void Move()
    {
        //获取当前蛇头移动的局部坐标
        headPos = gameObject.transform.localPosition;
        //将蛇头当前的移动位置加上x轴和y轴的移动增量,实现蛇头的移动
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, 0);
        
        //刚开始bodyList为空,防止报空指针
        if (bodyList.Count > 0)
        {
            for (int i = bodyList.Count - 2; i >= 0; i--)
            {
                //将前一节蛇尾的位置赋予后一节
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //将原来蛇头的位置赋予给下标为0的蛇尾,也就是蛇头后一节的蛇尾
            bodyList[0].localPosition = headPos;
        }

        //方法二:将蛇尾最后一节移至蛇头的位置
        //if (bodyList.Count > 0)
        //{
        //bodyList.Last()获取list最后的元素
        //    bodyList.Last().localPosition = headPos;
        //Insert将元素插入到指定位置
        //    bodyList.Insert(0, bodyList.Last());
        //RemoveAt移除指定下标的元素
        //    bodyList.RemoveAt(bodyList.Count - 1);
        //}
    }




  • 27
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
unity贪吃蛇游戏源码+AI-游戏源码: 1.利用ngui插件对界面进行了仿写 在仿写界面途中发现,贪吃蛇这个游戏在界面适应屏幕分辨率,所以在开发中需要注意界面对屏幕的适应性。通过每个图片的宽高 和uiroot的比值,在start方法中对他进行了缩放。 2.利用 ngui 自带的uidragdropItem实现摇杆 界面完成之后,首先对贪吃蛇的摇杆进行了编程。 在unity中的NGUI上,给sprite创建添加一个uidragdropitem 实现了sprite的拖动。 AI的开始,移动逻辑: 整体只考虑三个前景: 1.首先先判断蛇首的简介范围是否有其他蛇死亡了留下的食物,如果有的话设置当前朝向为星星方向,便设置移动倍率为2,否则判断第三种情况; 3.随机朝某个位置移动。如果每帧AI都在更新的话,蛇的反映速度依然会更快,需要设置一下AI的更新间隔,通过调整这个间隔和蛇首的简介方位来调整AI的反映速度,目的是能够让玩家有机会撞死AI。 AI 移动的方向分析: AI在一个平面上运动,坐标就只有两个<x,y> 把他用0和1表示为: ----- 4个方向 上下左右 (0,0) (0,1) (0,-1) (-1,0) (1,0) 四个角度 象限。 (-1,1) (1,1) (-1,-1) (1,-1) 设计思路就是把AI蛇也当作是用摇杆来控制的。那么AI就方便写多了,只是 在还没开始写之前,要考虑的就是AI蛇多了之后程序会不会卡死了。在使用线程控制的过程中发现了一个问题,cup利用率太高。所以想先尝试一下在update上操作。如果不行的在想其他的办法。 设计: 把每个AI蛇都相当于人在控制他。那么想象一下虚拟摇杆,用一个数组来存这些虚拟摇杆的位移坐标 把每条AI蛇的长度也记录下来,默认长度就20 错误: NullReferenceException: Object reference not set to an instance of an object AI6control.flootAIOneBody6 (Vector3 V) (at Assets/AI6control.cs:57) AI6control.AIsix () (at Assets/AI6control.cs:40) AI6control.Update () (at Assets/AI6control.cs:13) 该错误在百度上说:没有实例化的问题 最后一个下午的代码调试 发现由于自己的粗心 导致了把<符号写成了<=符号 就因为这个问题,让贪吃蛇变成了 卡神,害我看了好几天,现在解决了,贪吃蛇也变流畅了好多. Ai蛇也需要吃食物。长大,在游戏刚启动的时候就让Ai蛇在地图中吃食物,成长,当用户进入游戏的时候AI蛇已经吃到了一定的食物。测试了AI蛇确实发现他能吃食物。死后的处理也处理了。 在AI蛇吃食物的时候 ,地图中食物太多,控件太多。没法知道他是什么时候,查了资料说是要遍历地图中的对象,测试了一下,发现对象太多,程序卡得不要不要的,只能用触发器,避免遍历食物让程序运行不流畅或者变得死机等情况。 这样的话 在游戏中运行10-20条蛇是没有问题的。 地图中有10个点,这10个点是用来AI蛇死后复活的地方。 增加了限定时间模式. 经过很多的测试,也发现了很多的bug 不过都基本改完了,现在暂时没有发现bug
以下是用Unity3D引擎开发3D贪吃蛇游戏的示例代码: ```csharp using System.Collections.Generic; using UnityEngine; public class SnakeController : MonoBehaviour { public GameObject snakeHeadPrefab; public GameObject snakeBodyPrefab; public float moveSpeed = 0.1f; public float rotationSpeed = 5f; public int initialBodySegments = 3; private List<Transform> bodySegments = new List<Transform>(); private Vector3 moveDirection = Vector3.right; private bool canRotate = true; private bool isDead = false; void Start() { SpawnSnake(); } void Update() { if (!isDead) { MoveSnake(); CheckInput(); } } void SpawnSnake() { GameObject snakeHead = Instantiate(snakeHeadPrefab); bodySegments.Add(snakeHead.transform); for (int i = 0; i < initialBodySegments; i++) { AddBodySegment(); } } void AddBodySegment() { GameObject snakeBody = Instantiate(snakeBodyPrefab); bodySegments.Add(snakeBody.transform); } void MoveSnake() { for (int i = bodySegments.Count - 1; i > 0; i--) { bodySegments[i].position = bodySegments[i - 1].position; } bodySegments[0].position += moveDirection * moveSpeed; } void CheckInput() { if (canRotate) { if (Input.GetKeyDown(KeyCode.RightArrow)) { moveDirection = Quaternion.Euler(0f, 90f, 0f) * moveDirection; canRotate = false; RotateSnake(); } else if (Input.GetKeyDown(KeyCode.LeftArrow)) { moveDirection = Quaternion.Euler(0f, -90f, 0f) * moveDirection; canRotate = false; RotateSnake(); } } } void RotateSnake() { for (int i = 0; i < bodySegments.Count - 1; i++) { Vector3 targetDirection = bodySegments[i + 1].position - bodySegments[i].position; Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up); bodySegments[i].rotation = Quaternion.Slerp(bodySegments[i].rotation, targetRotation, rotationSpeed * Time.deltaTime); } } void OnTriggerEnter(Collider other) { if (other.CompareTag("Food")) { Destroy(other.gameObject); AddBodySegment(); } else if (other.CompareTag("Obstacle")) { isDead = true; Debug.Log("Game Over!"); } } } ``` 这个脚本实现了贪吃蛇的基本功能,包括移动、旋转、添加身体节、吃食物和撞到障碍物时游戏结束等。你需要在场景中设置好贪吃蛇头、身体和食物的Prefab,并将其分别赋值给snakeHeadPrefab、snakeBodyPrefab和foodPrefab变量。还需要在场景中添加障碍物,并将其标记为"Obstacle" Tag。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值