Unity之贪吃蛇游戏

10 篇文章 1 订阅

Unity之贪吃蛇游戏

大纲

UI制作:
1.Start界面
(0)背景:
(1)皮肤
(2)模式
(3)得分:长度,分数

2.Game界面
(0)背景
(1)阶段
(2)得分
(3)长度
(4)碰撞器边界

2.小蛇的行为活动:
(1)wasd移动蛇头,f加速
(2)吃食物,并使蛇身增长,(链表节点+1)
(3)使蛇身跟着一起移动(都为本地坐标)
(4)蛇死亡

3.食物系统:
(1)生成普通食物
(2)生成奖励

4.UI与数据交互
1.StartUI

(1)最高和上次的数据显示
(2)皮肤和模式选择:结合Toggle使用动态函数设置(Set)皮肤和边界的数据,注意皮肤是需要被加载的资源,设置的是文件名。 还需要再Start设置(Get)开始时的默认模式和皮肤。

2.GameUI
(1)背景变化
(2)暂停和回家
(3)是否有边界

3.Snake: 获取蛇的皮肤数据

新知识:
1.结合Toggle使用动态函数动态的获取Toggle的isOn;

2.使用PlayerPrefs.GetString(键,值)和PlayerPrefs.GetString(键)等储存和获取信息。

3.通过Resources.Load(string path)方法加载资源,注意文件名一定为Resources。

4.单例模式的使用

一.制作UI界面
导入资源。

1.Start界面
其中背景为image组件
开始游戏为一个按钮
皮肤,模式,得分,长度,分数为为Text;
皮肤,模式的选择用Toggle+Toggle Group控制
在这里插入图片描述

2.然后新建一个场景制作游戏时的UI

在这里插入图片描述

二.小蛇的行为活动

制作完了UI之后我们开始处理小蛇,首先我们先让它的蛇头动起来,首先创建一个image图片然后让拖入相应的蛇头文件,然后创建相应的脚本挂在小球上,脚本如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class snake : MonoBehaviour
{
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;

   
    private void Start()
{        
    speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step )
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step )
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F) )//加速
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed - 0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F))//停止加速
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed);
        }
    }
    /// <summary>
    /// 移动
    /// </summary>
    void Move()
    {

        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

      
    }
}

三.食物系统

由于小蛇的行为活动中的吃食物需要食物,所以我们先转到食物系统。

生成普通食物和随机奖励。

创建image组件并拖入食物图片,然后把它变成一个预制体,然后添加标签为food,同上创建奖励,添加reward标签,然后创建一个空组并命名为ScriptHoler,然后在上面挂载脚本名为makeFood的脚本。注意由于食物在游戏中时时存在且唯一,所以我们可以把它设置为单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class makeFood : MonoBehaviour
{
    public GameObject foodPerfatic;
public GameObject foodReward;
int xlimit,ylimit; //食物生成的位置的限定值
    int xoffset; //由于地图的横向位置不对称所以会有一个偏移量。
    int x, y;
    Transform canvas;获取画布以便设置父对象
    public Sprite[] foodSprites=  new Sprite[8];//食物的种类的照片


//单例模式
    static private makeFood _instant;
    static public makeFood Instant
    {
        get { return _instant; }
    }
  
   private void Awake()
    {
        _instant = this;
        canvas= GameObject.Find("Canvas").transform;//获取名字为Canvas的组件
    }
    
    void Start()
    {
        ylimit = 11;
        xlimit = 20 ;
        xoffset = 9;
        CreatFood();
    }

    // Update is called once per frame
   
  public  void CreatFood()
    {
        x = Random.Range(-(xlimit-xoffset),xlimit)*30;//Random.Range是前开后闭区间
        y = Random.Range(-ylimit, ylimit)*30;
        GameObject food = Instantiate(foodPerfatic);
        food.transform.SetParent(canvas, false);//设置生成食物的父对象,并使它保持未设置父对象之前的状态(缩放,旋转,坐标等值不变)
        int index = Random.Range(0, foodSprites.Length);
        food.GetComponent<Image>().sprite = foodSprites[index];//设置食物的图片
        food.transform.localPosition = new Vector3(x, y, 0);

        bool isnoreward = Random.Range(0, 100) < 20 ? true : false;//设置有1/5的概率生成奖励
        if (isnoreward)
        {
            x = Random.Range(-(xlimit - xoffset), xlimit) * 30;
            y = Random.Range(-ylimit, ylimit) * 30;
            GameObject reward = Instantiate(foodReward);
            reward.transform.SetParent(canvas, false);
            
            food.transform.localPosition = new Vector3(x, y, 0);
        }
    }
}

四.小蛇的行为活动

1.(吃食物)

生成食物后我们移动蛇头发现碰食物不能并能使食物消失,所以我们需要添加碰撞检测,

我们先给食物的预制体添加2d盒型碰撞器,并把它的范围x,y设置为一个比step(每次移动的距离)稍小的值,如果x,y比step大的话,就会出现擦边就吃到食物的现象,然后再勾上is Trigger。
然后再给蛇头添加2d盒型碰撞器,x,y的值同食物一样,然后再加上一个2d刚体并把它的Gravity Scale设置0。然后更新snake 的脚本如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class snake : MonoBehaviour
{
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;

   
    private void Start()
{        
    speed=0.5f
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step )
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step )
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F) )//加速
        {
          CancelInvoke();
            InvokeRepeating("Move", 0, speed - 0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F))//停止加速
        {
           CancelInvoke();
            InvokeRepeating("Move", 0, speed );
        }
    }
    /// <summary>
    /// 移动
    /// </summary>
    void Move()
    {

        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

      
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.tag == "food")
        {
            Destroy(collision.gameObject);
 makeFood.Instant.CreatFood();
           
        }
        else if (collision.tag == "reward")
        {
            Destroy(collision.gameObject);
           
        }
       


    }
}

2.(长身体和蛇身移动)

吃到食物后蛇身应该增长(实质就是给链表加一个节点),然后蛇身并随之移动(移动的核心思想就是把前一个蛇身的坐标赋值给后一个蛇身,然后把未移动前的蛇头坐标赋值给第一个蛇身。

然后更新snake 的脚本如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class snake : MonoBehaviour
{
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;


    List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
    public Transform bodyPrefab;//身体的预制体
    public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片

    Transform canvas;//获取画布以便设置父对象

    private void Start()
{
speed=0.5f
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
        canvas = GameObject.Find("Canvas").transform;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step )
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step )
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F))
        {
          CancelInvoke();
            InvokeRepeating("Move", 0, speed - 0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F) )
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed);
        }
    }
    /// <summary>
    /// 生长
    /// </summary>
    /// <param name="collision"></param>
    void Grow()
    {
        Transform newbody = Instantiate(bodyPrefab);
        newbody.transform.SetParent(canvas, false);
        int index = bodylist.Count % 2 == 0 ? 1 : 0;
        newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
        newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
        bodylist.Add(newbody);

    }

    void Move()
    {
        Debug.Log(transform.position);
        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

        if (bodylist.Count > 0)
        {
            for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
            {
                bodylist[i + 1].localPosition = bodylist[i].localPosition;
            }
            bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身

        }

    }
   

    public void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.tag == "food")
        {
            Destroy(collision.gameObject);
            Grow();
            makeFood.Instant.CreatFood();
            
        }
        else if (collision.tag == "reward")
        {
            Destroy(collision.gameObject);
            Grow();
        }

    }
}

运行效果如下

在这里插入图片描述

3.(死亡判定)
蛇死亡一种是撞到边界,一种是蛇头撞到蛇身。然后 游戏边界的UI部分已完成(实质上就是盒型碰撞器标签为borad),然后我们更新snake脚本如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class snake : MonoBehaviour
{
    //move函数
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
    Vector3 HeadPosition;//储存蛇头坐标
    List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
public static float speed;
    //grow函数
    public Transform bodyPrefab;//身体的预制体
    public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
    Transform canvas;//获取画布以便设置父对象

    //death函数
    bool isDia;//用于判断蛇是否死亡

    private void Start()
{
speed=0.5f
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
        canvas = GameObject.Find("Canvas").transform;

        isDia = false;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step&&!isDia)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step && !isDia)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step && !isDia)
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step && !isDia)
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F) && !isDia)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed-0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F) && !isDia)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed);
        }
    }
    /// <summary>
    /// 生长
    /// </summary>
    /// <param name="collision"></param>
    void Grow()
    {
        Transform newbody = Instantiate(bodyPrefab);
        newbody.transform.SetParent(canvas, false);
        int index = bodylist.Count % 2 == 0 ? 1 : 0;
        newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
        newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
        bodylist.Add(newbody);

    }

    void Move()
    {
        Debug.Log(transform.position);
        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

        if (bodylist.Count > 0)
        {
            for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
            {
                bodylist[i + 1].localPosition = bodylist[i].localPosition;
            }
            bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身

        }

    }
   
     public void death()
    {        
        isDia = true;
        CancelInvoke();//取消对move函数的调用
    }
    public void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.tag == "food")
        {
            Destroy(collision.gameObject);
            Grow();
            makeFood.Instant.CreatFood();
            
        }
        else if (collision.tag == "reward")
        {
            Destroy(collision.gameObject);
            Grow();
        }
        else if (collision.tag == "body")
        {
           death();
        }
        else
        {           
           death();
           
        }

    }
}

UI与数据交互

Game
(1)分数变化,游戏背景变化,蛇的速度变化
(2)暂停和回家按钮

创建一个脚本命名为GameUICtroll ,添加以下内容,并挂载到ScriptHoler上去,然后将各组件一一对应。值得注意的是GameUICtroll 也符合单例模式的要求,可以把其设置为单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameUICtroll : MonoBehaviour
{
    static private GameUICtroll _instant;
    static public GameUICtroll Instant
    {
        get { return _instant; }
    }

    public bool isPause;
    public int grade;
    public int Length;
    public Text ScoreText;
    public Text LengthText;
    public Text JieDuanText;
    public Transform BGM;//用于储存游戏背景图片
    public Sprite[] pauseSprite = new Sprite[2];
    public Transform PauseButton;
private Color temcolor;
   private void Awake()
    {

        //如果这里不初始时间流速,那么在游戏里按了暂停在返回家,再开始游戏就还会是暂停。
        Time.timeScale = 1;
        _instant = this;

 }

    void Start()
    {
        isPause = false;
        
        grade = 0;
    }
    /// <summary>
    /// 增加分数,以及分数增加后对游戏背景和小蛇速度的处理
    /// </summary>
    /// <param name="score"></param>
    /// <param name="length"></param>
    public void AddGrade(int score=10,int length=1) 
    {
        Length+=length;
        grade += score;
        switch (grade / 100)
        {
            case 2:
            case 3:
                snake.speed = 0.35f;
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out temcolor);//解析一个表示颜色的字符串
                BGM.GetComponent<Image>().color = temcolor;                
                JieDuanText.text = "阶段" + 2;
                break;
            case 4:
            case 5:
                snake.speed = 0.2f;
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out temcolor);
                BGM.GetComponent<Image>().color = temcolor;
                JieDuanText.text = "阶段" + 3;
                break;
            default:
                break;
        }
        ScoreText.text = "分数\n" + grade;
        LengthText.text = "长度\n" + Length;

    }
    /// <summary>
    /// 暂停功能
    /// </summary>
    public void Pause()
    {
        isPause = !isPause;
        if (isPause)
        {
            PauseButton.GetComponent<Image>().sprite = pauseSprite[1];
            Time.timeScale = 0;

        }
        else
        {
            PauseButton.GetComponent<Image>().sprite = pauseSprite[0];
            Time.timeScale = 1;
        }

    }
    /// <summary>
    /// 回到游戏开始界面
    /// </summary>
    public void Hoom()
    {
        SceneManager.LoadScene(0);
    }
}

然后我们在snake脚本里调用GameUICtroll 脚本里的AddGrade()函数,并记录死亡时的分数和设置死亡后重开的游戏协程等操作,更新如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class snake : MonoBehaviour
{
    //move函数
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
    Vector3 HeadPosition;//储存蛇头坐标
    List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
    public static float speed;
    //grow函数
    public Transform bodyPrefab;//身体的预制体
    public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
    Transform canvas;//获取画布以便设置父对象

    //death函数
    bool isDia;//用于判断蛇是否死亡

    private void Start()
    {
        speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
        canvas = GameObject.Find("Canvas").transform;

        isDia = false;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step&&!isDia&&GameUICtroll.Instant.isPause)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step && !isDia&&!GameUICtroll.Instant.isPause)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step && !isDia&&!GameUICtroll.Instant.isPause)
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step && !isDia&&!GameUICtroll.Instant.isPause)
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F) && !isDia&&!GameUICtroll.Instant.isPause)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed - 0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F) && !isDia&&!GameUICtroll.Instant.isPause)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed);
        }
    }
    /// <summary>
    /// 生长
    /// </summary>
    /// <param name="collision"></param>
    void Grow()
    {
        Transform newbody = Instantiate(bodyPrefab);
        newbody.transform.SetParent(canvas, false);
        int index = bodylist.Count % 2 == 0 ? 1 : 0;
        newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
        newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
        bodylist.Add(newbody);

    }

    void Move()
    {
        Debug.Log(transform.position);
        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

        if (bodylist.Count > 0)
        {
            for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
            {
                bodylist[i + 1].localPosition = bodylist[i].localPosition;
            }
            bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身

        }

    }
   
     public void death()
    {        
        isDia = true;
        CancelInvoke();//取消对move函数的调用
        //PlayerPrefs以键值对的形式将数据保存在文件中,我们可以根据名称取出上次保存的值
        PlayerPrefs.SetInt("LastS", GameUICtroll.Instant.grade);
        PlayerPrefs.SetInt("LastL",GameUICtroll.Instant.Length);
        if (PlayerPrefs.GetInt("LastS", 0) > PlayerPrefs.GetInt("BestS", 0))
        {
            PlayerPrefs.SetInt("BestS", GameUICtroll.Instant.grade);
            PlayerPrefs.SetInt("BestL", GameUICtroll.Instant.Length);
        }
        StartCoroutine("Die");
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "food")
        {
            Destroy(collision.gameObject);
            Grow();
            makeFood.Instant.CreatFood();
            
        }
        else if (collision.tag == "reward")
        {
            Destroy(collision.gameObject);
            Grow();
        }
        else if (collision.tag == "body")
        {
           death();
        }
        else
        {           
           death();
           
        }

    }
    IEnumerator Die()
    {

        yield return new WaitForSeconds(1.5f);

        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

Start界面
(1)皮肤和模式选择:结合Toggle使用动态函数设置(Set)皮肤和边界的数据,注意皮肤是需要被加载的资源,设置的是文件名。 还需要再Start设置(Get)开始时的默认模式和皮肤。
(2)最高和上次的数据显示

新建脚本名为StartUI的脚本,并创建一个空对象ScriptGet。把脚本挂载到上面,脚本内容如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class StartUI : MonoBehaviour
{

    public Toggle blue;
    public Toggle yellow;
    public Toggle broad;
    public Toggle noBroad;
    public Text LastText;
    public Text BestText;
    void Start()
    {
        //显示上次和最高的分数
        LastText.text = "上次: " + "分数 " + PlayerPrefs.GetInt("LastS") + "长度 " + PlayerPrefs.GetInt("LastL");
        BestText.text = "最高: " + "分数 " + PlayerPrefs.GetInt("BestS") + "长度 " + PlayerPrefs.GetInt("BestL");

        //设置刚开始的默认皮肤和用户选择了其它皮肤游戏结束时默认还是进入游戏时选择的皮肤
        if (PlayerPrefs.GetString("sh","sh01")=="sh01")
        {
            blue.isOn = true;
          
        }
        else
        {
            yellow.isOn = true;
        }

        //模式同皮肤一样
        if (PlayerPrefs.GetInt("borad", 0) ==1)
        {
            broad.isOn = true;

        }
        else
        {
            noBroad.isOn = true;           
        }
    }

    //该函数连接Toggle组件,当Toggle组件isOn变化时会动态的返回Toggle组件isOn的值
    public void Blue(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh","sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }

    }
    public void Yellow(bool isOn)
    {
        if (isOn)
        {
            
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }

    }
    public void Classica(bool isOn)
    {
        if (isOn)
        {
           
            PlayerPrefs.SetInt("borad", 1);
           
        }

    }
    public void Free(bool isOn)
    {

        if (isOn)
        {       
            PlayerPrefs.SetInt("borad", 0);

        }
    }
    public void GoToGame()
    {
        SceneManager.LoadScene(1);
    }
    
   
}

然后使该脚本定义的UI与我们创建的UI一一对应。
在这里插入图片描述

最后我们根据用户的选择动态的返回数据到游戏里。
GameUICtroll 脚本更新如下。主要是对是否有无边界的处理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameUICtroll : MonoBehaviour
{
    static private GameUICtroll _instant;
    static public GameUICtroll Instant
    {
        get { return _instant; }
    }

    public bool isPause;
    public int grade;
    public int Length;
    public Text ScoreText;
    public Text LengthText;
    public Text JieDuanText;
    public Transform BGM;
    public Sprite[] pauseSprite = new Sprite[2];
    public Transform PauseButton;
    private Color temcolor;

    public bool isnoborad;   //储存从用户获取是否有无边界
    private void Awake()
    {

        if (PlayerPrefs.GetInt("borad") == 0)
        {
            isnoborad = false;

            foreach (Transform item in BGM)//把背景的边界去掉,也就是背景下四个碰撞器上的Image组件停用
            {
                item.GetComponent<Image>().enabled = false;
            }
        }
        else
        {
            isnoborad = true;
        }
        //如果这里不初始时间流速,那么在游戏里按了暂停在返回家,再开始游戏就还会是暂停。
        Time.timeScale = 1;
        _instant = this;

    }
    void Start()
    {
        isPause = false;
        
        grade = 0;
    }
    /// <summary>
    /// 增加分数,以及分数增加后对游戏背景和小蛇速度的处理
    /// </summary>
    /// <param name="score"></param>
    /// <param name="length"></param>
    public void AddGrade(int score=10,int length=1) 
    {
        Length+=length;
        grade += score;
        switch (grade / 100)
        {
            case 2:
            case 3:
                snake.speed = 0.35f;
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out temcolor);//解析一个表示颜色的字符串
                BGM.GetComponent<Image>().color = temcolor;                
                JieDuanText.text = "阶段" + 2;
                break;
            case 4:
            case 5:
                snake.speed = 0.2f;
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out temcolor);
                BGM.GetComponent<Image>().color = temcolor;
                JieDuanText.text = "阶段" + 3;
                break;
            default:
                break;
        }
        ScoreText.text = "分数\n" + grade;
        LengthText.text = "长度\n" + Length;

    }
    /// <summary>
    /// 暂停功能
    /// </summary>
    public void Pause()
    {
        isPause = !isPause;
        if (isPause)
        {
            PauseButton.GetComponent<Image>().sprite = pauseSprite[1];
            Time.timeScale = 0;

        }
        else
        {
            PauseButton.GetComponent<Image>().sprite = pauseSprite[0];
            Time.timeScale = 1;
        }

    }
    /// <summary>
    /// 回到游戏开始界面
    /// </summary>
    public void Hoom()
    {

        SceneManager.LoadScene(0);

    }
}

snake脚本里接收(皮肤)数据。snake脚本更新如下。其中我的Resources文件夹的文件如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class snake : MonoBehaviour
{
    //move函数
    int step;//每次移动的距离
    int x, y;//蛇横纵需要移动的距离
    Vector3 HeadPosition;//储存蛇头坐标
    List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
    public static float speed;
    //grow函数
    public Transform bodyPrefab;//身体的预制体
    public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
    Transform canvas;//获取画布以便设置父对象

    //death函数
    bool isDia;//用于判断蛇是否死亡

    private void Start()
    {
        //Resources.Load(string name);通过文件名加载文件名为Resources里的文件
        transform.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh"));
        bodySprite[0] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02"));
        bodySprite[1] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01"));

        speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
        step = 30;
        InvokeRepeating("Move", 0, 0.3f);
        x = step;
        canvas = GameObject.Find("Canvas").transform;

        isDia = false;
    }
    private void Update()
    {
        if (Input.GetKey(KeyCode.W) && y != -step&&!isDia)//蛇的移动不能反向,所以需要y != -step的判断
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
            y = step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.S) && y != step && !isDia)
        {

            transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
            y = -step;
            x = 0;
        }
        else if (Input.GetKey(KeyCode.A) && x != step && !isDia)
        {
            transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
            y = 0;
            x = -step;
        }
        else if (Input.GetKey(KeyCode.D) && x != -step && !isDia)
        {
            transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
            y = 0;
            x = step;
        }


        if (Input.GetKeyDown(KeyCode.F) && !isDia)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed - 0.45f);
        }
        if (Input.GetKeyUp(KeyCode.F) && !isDia)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, speed);
        }
    }
    /// <summary>
    /// 生长
    /// </summary>
    /// <param name="collision"></param>
    void Grow()
    {
        Transform newbody = Instantiate(bodyPrefab);
        newbody.transform.SetParent(canvas, false);
        int index = bodylist.Count % 2 == 0 ? 1 : 0;
        newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
        newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
        bodylist.Add(newbody);

    }

    void Move()
    {
        Debug.Log(transform.position);
        HeadPosition = transform.localPosition;
        transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);

        if (bodylist.Count > 0)
        {
            for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
            {
                bodylist[i + 1].localPosition = bodylist[i].localPosition;
            }
            bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身

        }

    }
   
     public void death()
    {        
        isDia = true;
        CancelInvoke();//取消对move函数的调用
        //PlayerPrefs以键值对的形式将数据保存在文件中,我们可以根据名称取出上次保存的值
        PlayerPrefs.SetInt("LastS", GameUICtroll.Instant.grade);
        PlayerPrefs.SetInt("LastL",GameUICtroll.Instant.Length);
        if (PlayerPrefs.GetInt("LastS", 0) > PlayerPrefs.GetInt("BestS", 0))
        {
            PlayerPrefs.SetInt("BestS", GameUICtroll.Instant.grade);
            PlayerPrefs.SetInt("BestL", GameUICtroll.Instant.Length);
        }
        StartCoroutine("Die");
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "food")
        {
            Destroy(collision.gameObject);
            Grow();
            makeFood.Instant.CreatFood();
            
        }
        else if (collision.tag == "reward")
        {
            Destroy(collision.gameObject);
            Grow();
        }
        else if (collision.tag == "body")
        {
           death();
        }
        else
        {
            if (GameUICtroll.Instant.isnoborad)
            {
                death();
            }
            else
            {
                //自由模式无边界
                switch (collision.name)
                {
                    //当碰到碰撞器名字为up的碰撞器,注意使蛇头的位置到最下面的边界在向上一个身位,
                    //如果不向上移动一个身位他就会碰到最下面一个边界的碰撞器。如此往复
                    case "up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 30, 0);
                        break;
                    case "down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 30, 0);
                        break;
                    case "left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 9 * 30 - 30, transform.localPosition.y, 0);
                        break;
                    case "right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 9 * 30 + 30, transform.localPosition.y, 0);
                        break;
                    default:
                        break;
                }
            }

        }

    }
    IEnumerator Die()
    {

        yield return new WaitForSeconds(1.5f);

        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

最后游戏基本完成,效果如下。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 15
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值