2018-08-25 02:16:02 IT_CREATE 阅读数 2476

源码资源https://download.csdn.net/download/it_create/10625733

开始界面

运行界面

MainUIController.cs

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

public class MainUIController : MonoBehaviour {
    private static MainUIController _instance;//定义一个静态的私有MainUIController型的变量
    public static MainUIController Instance //提供一个外部访问的静态成员函数,其他脚本无法修改,只能访问,返回一个_instance
    {
        get
        {
            return _instance;
        }
    }
    public int score = 0;//定义分数变量
    public int length = 0;//定义长度变量
    public Text msgText; //定义模式信息文本变量,在界面显示,public的变量可以在unity3d操作界面进行指代
    public Text scoreText; //定义分数文本变量,在界面显示
    public Text lengthText; //定义长度文本变量,在界面显示
    public Image bgImage; //定义Image类型的变量,指代的就是界面上的Bg
    private Color tempColor; //定义颜色变量
    public bool isPause = false;//定义暂停的布尔类型变量,用于判断是否暂停,默认没有暂停
    public Image pauseImage; //定义暂停/继续背景图片
    public Sprite[] pauseSprites = new Sprite[2]; //定义暂停/继续的图片组
    public bool hasboder = true;//定义边界变量,默认是有边界的


    void Awake()//开始就唤醒给_instance赋值,不然_istance指向null
    {
        _instance = this;
    }

    void Start()
    {
        if(PlayerPrefs.GetInt("border",1) == 0) //默认是有边界的,如果等于0就相当于调到了自由模式
        {
            hasboder = false; //变成false就是设成没有边界
            foreach(Transform t in bgImage.gameObject.transform)//遍历bgImage下的所有子物体,交给t
            {
                t.gameObject.GetComponent<Image>().enabled = false; //在主场景中bg下的子物体就是上下左右四个边界,这里就是禁用四个边界,即没有边界
            }
        }
    }
	
	void Update ()
    {
		switch(score/100)//判断分数达到不同值改变它的颜色并改变显示它处于不同的阶段
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);//将改变后的颜色解析输出到tempColor
                bgImage.color = tempColor; //将tempColor赋给bgImage,在显示界面看到效果
                msgText.text = "阶段" + 2; //在显示界面显示阶段2
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CDFFDBEE", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#EBFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#FFF3CCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            default:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "无尽模式";
                break;
        }
	}
    public void UpdateUI(int s = 5,int l = 1) //分数更新函数,默认情况下吃到一个食物分数加5,长度加1
    {
        score += s;
        length += l;
        scoreText.text = "得分:\n" + score; //在显示界面显示得分
        lengthText.text = "长度:\n" + length; //在显示界面显示长度
    }

    public void pause()//暂停/继续函数
    {
        isPause = !isPause; //暂停状态取反
        if (isPause)
        {
            Time.timeScale = 0;//时间冻结
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;//时间解冻继续
            pauseImage.sprite = pauseSprites[0];
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);//加载0号场景,回到首页
    }
}

StartUIController.cs

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

public class StartUIController : MonoBehaviour {

    public Text lastText; //定义上次得分、长度文本变量,对应的是开始场景上的“上次”文本处
    public Text bestText; //定义最佳得分、长度文本变量,对应的是开始场景上的“最佳”文本处
    public Toggle blue; //这是个组件,定义组合按钮blue,在开始场景中它是加入了组的,在同组中相当于选择的时候只有一个可以被选中,它和yellow是互斥的
    public Toggle yellow;
    public Toggle border;
    public Toggle noborder;

    void Start() //开始函数,一开始就执行
    {
        if(PlayerPrefs.GetString("sh","sh01") == "sh01")//读取保存的信息同sh01比对,如果没有选择,默认是sh01给sh,这里是判断是否是小蓝蛇,GetString就是读取setString保存的信息的
        {
            blue.isOn = true; //条件满足的条件下,true就是将开始场景中blue勾选的意思
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true; //条件满足的条件下,true就是将开始场景中yellow勾选的意思
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if(PlayerPrefs.GetInt("border",1) == 1)//GetInt就是读取SetInt保存的信息的
        {
            border.isOn = true; //条件满足的条件下,true就是将开始场景中的boder勾选的意思
            PlayerPrefs.SetInt("border", 1);
        }
        else
        {
            noborder.isOn = true;//条件满足的条件下,true就是将开始场景中的noboder勾选的意思
            PlayerPrefs.SetInt("border", 0);
        }
    }

    void Awake()//唤醒函数
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("lastl", 0) + ",分数" + PlayerPrefs.GetInt("lasts", 0);//在开始场景显示上次的记录,这里是从保存的记录中获取,默认的是没玩过是0,玩过之后有记录就会被覆盖
        bestText.text = "最佳:长度" + PlayerPrefs.GetInt("bestl", 0) + ",分数" + PlayerPrefs.GetInt("bests", 0);// 在开始场景显示最佳的记录,这里是从保存的记录中获取,默认的是没玩过是0,玩过之后有记录就会被覆盖
    }

    public void BlueSelected(bool isOn)//小蓝蛇选择函数,这种类型的函数是挂载到开始界面blue上的,此函数相当于是定义了小蓝蛇的样子并保存起来它的属性
    {
        if(isOn)
        {
            //将蛇头和后面两个蛇身保存起来,这里保存的白蓝色蛇身,相当于小蓝蛇,此函数相当于是定义了小蓝蛇的样子并保存起来它的属性
            PlayerPrefs.SetString("sh", "sh01");//SetString就是保存的意思,这保存的是蛇头信息
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");

        }
    }
    public void YellowSelected(bool isOn) //小黄蛇选择函数,这种类型的函数是挂载到开始界面yellow上的,此函数相当于是定义了小黄蛇的样子并保存起来它的属性
    {
        if (isOn)
        {
            //将蛇头和后面两个蛇身保存起来,这里保存的蓝黄色蛇身,相当于小黄蛇,此函数相当于是定义了小黄蛇的样子并保存起来它的属性
            PlayerPrefs.SetString("sh", "sh02");//SetString就是保存的意思,这保存的是蛇头信息
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }
    public void BoderSelected(bool isOn) //边界模式选择,这种类型的函数是挂载到开始界面border上的,此函数相当于是定义了边界模式的属性并保存起来它的属性
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 1);//设置为边界模式,并且定义了boder等于1时为边界模式,setInt就是保存的意思
        }
    }
    public void NoBoderSelected(bool isOn)//自由模式选择,这种类型的函数是挂载到开始界面noboder上的
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 0);//设置为无边界(自由)模式,并且定义了boder等于0时为自由模式,setInt就是保存的意思
        }
    }

    public void StartGame()//开始游戏函数,对应开始场景上的开始按钮
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);//加载1号场景,即main场景
    }

}

foodmaker.cs

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

public class foodmaker : MonoBehaviour {
    private static foodmaker _instance;//定义一个静态的私有foodmaker型的变量
    public static foodmaker Instance //提供一个外部访问的静态成员函数,其他脚本无法修改,只能访问,返回一个_instance
    {
        get
        {
            return _instance;
        }
    }

    public int xlimit = 27;//定义食物x轴方向的最大限制,一个单位相当于一个步长step的大小,这个值相当于是坐标上具体的值除以步长得到的值,因为蛇头运动是按照一个步长一个步长的瞬间移动
    public int ylimit = 10;//定义食物y轴方向最大的限制,y轴上下都是10步,整个显示区是呈现x,y轴左右对称的,坐标轴原点在显示区中心
    public int xoffset = 10; //这个是左边分数、模式显示区x轴方向的步长宽度
    public GameObject foodPrefab; //定义食物的预制体
    public GameObject rewardPrefab; //定义奖励预制体
    public Sprite[] foodSprites;  //定义食物的图片
    private Transform foodHolder; //定义放置食物位置的容器

    void Awake() //开始就唤醒给_instance赋值,不然_istance指向null
    {
        _instance = this;
    }

	void Start () {
        foodHolder = GameObject.Find("FoodHolder").transform;//查找到FoodHolder这个定义的容器的位置
        MakeFood(false);//调用食物生成函数,开始时不生成奖励
	}
	void Update () {
		
	}
    public void MakeFood(bool isReward)//生成食物函数,里面的布尔类型是用来判断是否生成奖励
    {
        int index = Random.Range(0, foodSprites.Length);//从食物图片中随机选出一个,保存随即图片的索引值
        GameObject food = Instantiate(foodPrefab); //实例化食物预制体
        food.GetComponent<Image>().sprite = foodSprites[index];//获取到食物预制体身上的图片组件,将随机选出的图片挂载到食物上
        food.transform.SetParent(foodHolder, false); //设置食物的父物体为foodHolder,就是将food放置在foodHolder容器下面
        int x = Random.Range(-xlimit + xoffset, xlimit); //设置食物在x轴上的范围
        int y = Random.Range(-ylimit, ylimit); //设置食物在y轴上的范围
        food.transform.localPosition = new Vector3(x * 15, y * 15, 0);//设置食物的随机生成具体位置,乘以15是因为步长为15,保证蛇头能够触碰到
        if(isReward)
        {
            GameObject reward = Instantiate(rewardPrefab); //实例化奖励预制体
            reward.transform.SetParent(foodHolder, false); //设置奖励的父物体为foodHolder,就是将food放置在foodHolder容器下面
            x = Random.Range(-xlimit + xoffset, xlimit); //设置奖励在x轴上的范围
            y = Random.Range(-ylimit, ylimit); //设置奖励在y轴上的范围
            reward.transform.localPosition = new Vector3(x * 15, y * 15, 0);//设置奖励的随机生成具体位置,乘以15是因为步长为15,保证蛇头能够触碰到

        }
    }
}

SnakeHead.cs

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

public class SnakeHead : MonoBehaviour {
    public int step;//建立蛇头一步的距离的变量
    public float velocity=0.35f;//设置蛇头的速度
    private int x;
    private int y;
    private Vector3 headpos;//建立蛇头的当前位置变量
    public GameObject bodyprefab;//蛇身的预制体
    public List<Transform> bodyList = new List<Transform>();//用于存放蛇身位置的链表
    public Sprite[] bodySprites = new Sprite[2];//指定生成的长度为2,即这个数组存放的蛇身的颜色有两种
    public Transform canvas; //定义放置蛇身位置的容器
    private bool isDie = false; //定义是否死亡的布尔变量,默认是没有死亡false
    public GameObject dieEffect;//定义死亡爆炸特效
    public AudioClip eatClip; //定义吃食物音效
    public AudioClip dieClip; //定义死亡音效

    void Start()
    {
        InvokeRepeating("move", 0, velocity);//设置重复调用函数move,实现蛇头不停的运动,每隔0.35秒调用一次
        x = 0; y = step;//初始设置x,y值
    }

    void Awake()//唤醒函数
    {
        canvas = GameObject.Find("Canvas").transform;//查找到Canvas这个容器的位置
        //通过Resources.Load<string path>方法加载资源,path的书写不需要加 Resources/以及文件扩展名
        gameObject.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh", "sh02"));
        bodySprites[0] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01", "sb0201"));
        bodySprites[1] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02", "sb0202"));
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false && isDie ==false)//获取按键空格键按下操作,且不处于暂停状态,且不处于死亡状态
        {
            CancelInvoke();//取消当前调用的InvokeRepeating
            InvokeRepeating("move", 0, velocity - 0.3f);//设置新的函数,改变蛇头运动的频率
        }
        if(Input.GetKeyUp(KeyCode.Space)&&MainUIController.Instance.isPause == false && isDie == false)//获取按键空格键抬起操作
        {
            CancelInvoke();//取消当前调用的InvokeRepeating
            InvokeRepeating("move", 0, velocity);//设置新的函数,改变蛇头运动的频率,退回到原始频率
        }
        if (Input.GetKey(KeyCode.W) && MainUIController.Instance.isPause == false && isDie == false)//获取按键W,按下w,设置蛇头向上的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);//蛇头原始的方向
            x = 0;y = step; 
        }
        if (Input.GetKey(KeyCode.S) && MainUIController.Instance.isPause == false && isDie == false)//获取按键S,按下s,设置蛇头向下的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);//改变蛇头的方向,向下旋转180度
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && MainUIController.Instance.isPause == false && isDie == false)//获取按键A,按下a,设置蛇头向左的偏移量        

        { gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90); //改变蛇头的方向,向左旋转90度
            x = -step; y = 0;
        }
        if (Input.GetKey(KeyCode.D) && MainUIController.Instance.isPause == false && isDie == false)//获取按键D,按下d,设置蛇头向右的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90); //改变蛇头的方向,向右旋转90度
             x = step; y = 0;
        }
    }

    void move ()//设置蛇头移动的函数
    {
        headpos = gameObject.transform.localPosition;//获取蛇头当前的位置保存下来
        gameObject.transform.localPosition = new Vector3(headpos.x + x ,headpos.y +y,headpos.z);//设置每次移动后改变的位置
        if(bodyList.Count >0)
        {
            //由于是双色蛇身,应用此方法保证蛇身颜色的顺序保持不变动
            for(int i = bodyList.Count -2; i>=0; i--)//从后往前移动蛇身
            {
                bodyList[i + 1].localPosition = bodyList[i].localPosition;//每一个蛇身移动到它的前一个蛇身的位置
            }
            bodyList[0].localPosition = headpos;//第一个蛇身移动到蛇头移动前的位置
        }
    }

    void Grow()//生成蛇身
    {
        AudioSource.PlayClipAtPoint(eatClip, Vector3.zero); //播放吃食物音效
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;//因为蛇身的颜色是交叉呈现的,所以蛇身的颜色是两种不同的,即蛇身的奇偶部分呈现交叉,所以给奇偶部分赋值0或1两个索引
        GameObject body = Instantiate(bodyprefab,new Vector3(2000,2000,0),Quaternion.identity);//实例化蛇身在屏幕看不见的位置
        body.GetComponent<Image>().sprite = bodySprites[index];//获取蛇身上的Image组件给他附上图片
        body.transform.SetParent(canvas, false); //设置蛇身的父物体为canvas,就是将蛇身放置在canvas容器下面
        bodyList.Add(body.transform);//将生成的蛇身添加进这个链表里
    }

    public void Die() //死亡函数
    {
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero); //播放死亡音效
        CancelInvoke();//取消移动
        isDie = true;
        Instantiate(dieEffect);//实例化爆炸粒子特效 
        PlayerPrefs.SetInt("lastl", MainUIController.Instance.length);//保存最后一次长度,将length存放在lastl中,这个PlayerPrefs类是系统有的
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);//保存最后一次得分,将score存放在lasts中
        if(PlayerPrefs.GetInt("bests",0)<MainUIController.Instance.score)//判断最好的得分是否小于当前得分,PlayerPrefs.GetInt("bests",0)表示在初始的时候没玩过的时候,默认是0,之后就会用bests替换
        {
            PlayerPrefs.SetInt("bestl", MainUIController.Instance.length);//如果满足了条件,就会将当前长度替换保存在bestl中
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);//如果满足了条件,就会将当前得分替换保存在bests中
        }
        StartCoroutine(GameOver(1.5f)); //调用协程函数GameOver
    }

    IEnumerator GameOver(float t) //协程函数(分步执行,遇到条件(yield return)会挂起,直到条件满足才会被唤醒继续执行后面的代码
    {
        yield return new WaitForSeconds(t);//等待t秒后继续执行下面
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);//重载当前场景
    }

    private void OnTriggerEnter2D(Collider2D collider)//检测碰撞并传入碰撞信息
    {
        if (collider.gameObject.CompareTag("food")) //检测碰撞到的是不是标签为food
        {
            Destroy(collider.gameObject); //销毁碰撞到的物体
            MainUIController.Instance.UpdateUI(); //调用MainUIController中的UpdateUI()函数,就是分数更新
            Grow();
            foodmaker.Instance.MakeFood(Random.Range(0,100)<20?true:false); //通过访问脚本foodmaker公有的Instance并调用里面的MakeFood函数生成新的食物,有20%的几率生成奖励
        }
        else if(collider.gameObject.CompareTag("Reward")) //检测碰撞到的是奖励
        {
            Destroy(collider.gameObject); //销毁碰撞到的物体
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);//随机加上50到150的分数,长度默认加1
            Grow();
        }
        else if(collider.gameObject.CompareTag("body"))//检测碰撞到身体后死亡
        {
            Die();
        }
        else//检测到碰撞到四面墙壁后的后续反应
        {
            if(MainUIController.Instance.hasboder)
            {
                Die();
            }
            else
            {
                switch (collider.gameObject.name)
                {
                    case "up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 15, transform.localPosition.z);
                        //碰撞到上面的时候改变蛇头的位置,因为上下墙面对称,所以直接取相反数,加上15是在改变成相反位置后加上一个步长,因为改变方向后他又会和下面墙碰撞,所以加一个步长避免和下墙碰撞
                        break;
                    case "down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 15, transform.localPosition.z);
                        //碰撞到下面的时候改变蛇头的位置,因为上下墙面对称,所以直接取相反数,减15是在改变成相反位置后减一个步长,因为改变方向后他又会和上面墙碰撞,所以减一个步长避免和上墙碰撞
                        break;
                    case "left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 135, transform.localPosition.y, transform.localPosition.z);
                        //碰撞到左面的时候改变蛇头的位置,因为左右墙面存在着10个步长的距离,所以直接去相反数,加上135是在改变成相反位置后加上9个步长,少的那个步长是避免和右墙碰撞
                        break;
                    case "right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 165, transform.localPosition.y, transform.localPosition.z);
                        //碰撞到右面的时候改变蛇头的位置,因为左右墙面存在着10个步长的距离,所以直接去相反数,加上165是在改变成相反位置后加上11个步长,多的那个步长是避免和左墙碰撞
                        break;
                }
           }
            
        }
    }
}

 

2017-10-11 20:51:30 xujin12368 阅读数 3272

首先,我们需要做一个完整的2D贪吃蛇游戏策划:

  1. 游戏的策划
    本游戏使用Unity3D游戏引擎作为开发工具,以C#作为开发语言。运行平台为Windows。游戏的操作方式为玩家通过方先建控制贪吃蛇的移动,躲避炸弹或者进入传送门。只要蛇碰到了自己的身体或者地图边界,那么游戏失败。
  2. 使用Unity开发游戏前的准备工作
    (1)首先介绍的是本游戏中所用到的背景、按钮和数字图片资源,所有的图片资源全部放在Assets/Textures文件夹下。
    (2)然后介绍的是本游戏中所用到的音效资源,所有音效资源全部放在Assets/Sounds文件夹下
  3. 游戏的架构
    (1)主菜单场景MainMenu是转向游戏的中心场景。该场景包含了主摄像机和游戏背景。通过单击使用GUI生成的开始游戏按钮或者游戏设置按钮可以进入相应的部分。
    (2)游戏场景SnakeGame是本游戏中最重要的一个场景。在该场景中有多个游戏对象,主要包括主摄像机、计分板、贪吃蛇游戏对象、炸弹&传送门、游戏背景、音乐等。

 

2019-03-29 23:35:25 li1164407295 阅读数 558

    从开始学习到现在也做了不少小游戏,但是很多都堆积再同一个项目中,并且还有许多不完善之处,所以最近会逐渐发上来一些之前做的小游戏,希望对大家有所帮助。

    今天发上来的是最开始做的一个,也是挺经典的~贪吃蛇。

    之前我也在网上看过贪吃蛇的实现方法,其实主要的实现方法就是两种

  •     第一种就是仿照最原始的贪吃蛇制作的,其原理就是不断地把最后一个身体关节移至当前头部的前面,或者依次将所有身体关节移至前一身体关节的位置,以达到移动的效果。
  •     第二种也就是我采用的,(和第一种方法有点相似)将身体关节不断地向前一个身体关节的位置做插值处理,这样可以实现一条软软的蛇的效果。-------(我也不知道贪吃蛇大作战那些蛇是怎么做的,不过我感觉可能也是类似的方法。

在这提前放一下最后的一个实现效果:

小蛇在此

    预览视频在这

    同时你可以在这里下载到此项目,项目中注释还是很多的,不过其中有部分英文(练英语用的),大家可以直接去获取。

大致的解说:

    首先在这个项目中,我把游戏难度设置的比较低,死亡条件只有蛇头碰触到蛇身,期间,角色可以通过吞食场景中随机出现的食物来生长(积分系统没有做,emmm,绝对不是因为懒!)。并且角色还可以穿越墙壁到另一边墙,来做一个穿越。

    emmmmm,解说差不多就这么多,很简单的一个项目,上面链接处是我的github项目主页,里面还有一些我编的小工具什么的,希望大家喜欢~

 

奥对!!!!!

很重要的一件事!!

    目前这个项目还有一个BUG。当角色连续在四个角落进行穿越的时候,角色的身体会断掉………………我为这个BUG做了个修补……它的身体断掉超过1s时,会重新连接……但是还是会产生中断! 希望哪位腿粗的大大佬帮帮忙,看有没有什么办法能修补的。欢迎留言告知!!!!

   

 

2018-10-01 02:22:00 weixin_30919919 阅读数 6

 

 

Unity制作贪吃蛇小游戏

 

  玩家通过“WASD”控制小蛇上下左右移动,蛇头撞倒食物,则食物被吃掉,蛇身体长一节,接着又出现食物,等待蛇来吃,如果蛇在移动中撞到墙或身体交叉蛇头撞倒自己身体游戏结束

  可通过游戏开始前对小蛇皮肤进行选择

  自由模式下蛇头可以穿过四周的墙

  使用本地持久化保存与读取的类——PlayerPrefs类对玩家游戏数据的存储

 

  PlayerPrefs类存储位置  传送门

  Unity圣典  传送门

  游戏项目已托管到Github上  传送门

 

 

游戏展示

 

对玩家的游戏数据记录

   游戏界面存储玩家最高分以及上一次游戏的分数

 

 

 

游戏换肤

   为玩家可以选择蓝色小蛇或黄色小蛇

 

 

 

游戏模式

  边界模式下蛇撞到边界判定游戏结束

  自由模式下蛇碰到边界时会从另一个边界线出来

 

 (文字最下边有游戏脚本源代码)

 

  

 

 实现过程

 

制作开始场景UI界面

 

  添加一个Canvas作为游戏开始场景并将Canvas下的Render Mode设置为Screen Space—Camera

    1:Screen Space-Overlay:这种模式层级视图中不需要任何的摄像机,且UI出现在所有摄像机的最前面。
    2:Screen Space-Camera:这种模式需要绑定一个UICamrea,它支持UI前面显示3D对象和粒子系统。
    3:World Space:这种模式,UI和3d对象完全一样。
Canvas 三种渲染模式如下

 

(2D游戏常用Screen Space-Camera模式,3D游戏常用Screen Space-Overlay模式)

  

  Canvas Scaler(Script)个人比较喜欢设置UI Scale Mode设置成Scale With Screen Si   根据屏幕尺寸来调整UI的缩放值 

 

  指定渲染摄像机

 

  添加游戏背景及控制面板

 

  使用UGUI制作游戏场景界面

  

  Bg(Image):制作游戏背景界面

       ControlPanel(Panel):制作游戏选项区域

  Title(Text):制作游戏标题

  Go(Image):制作游戏开始图标

      添加Button控件,制作成按钮

      添加Outline外边框和Shadow阴影组件(逼真图片按钮)

  Text:"开始"文本

 

  添加十张Image作为游戏背景图片

 

  食物作为游戏背景太鲜明时,可以把食物的透明度调为150

 

  制作Text文字背景

 

   Mode下添加一个Toggle提供玩家对模式的选择

(给文字添加Outline给玩家一种朦胧感)

  

  边界模式下,蛇撞墙后判断游戏结束

  复制边界模式Toggle,修改文字为自由模式(自由模式下蛇撞墙后不会判定结束游戏)

  边界模式和自由模式玩家只能选择一个

 

  给Mode添加Toggle Group组件

  

 

  将Toggle Group组件绑定给Border和NoBorder

     

 

 

  is on :当前标签是否勾选

 

  同理添加小蛇皮肤属性

 

  将选择小蛇皮肤也设置成Toggle Group

  "黄色小蛇"和"自由模式" is on 去掉勾选

 

 

 

游戏场景UI界面

 

 

  ControlPanel下的按钮、文本控件

    Msg(Text):玩家选择游戏模式

    Score(Text):玩家当前得分

    Length(Text):当前蛇自身长度

    Home(Image):返回主页面按钮

    Pause(Image):停止游戏按钮

 

 

给游戏场景添加边界

 

  Bg下创建两个GameObject,设置(锚点)GameObject范围,添加Box Collider 2D作为游戏碰撞器

 

  给小蛇添加活动范围,添加一个Box Collider 2D碰撞器

  为了将活动范围标记出来,可以给碰撞其添加一个Image红色图片作为围墙

 

  修改一下碰撞器范围

 

 

制作贪吃蛇舌头并让其移动

 

  添加Image制作舌头,设置好舌头大小

 

  创建SnakeHead.cs脚本并绑定到蛇头部

 

  控制小蛇头部移动脚本

 void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
    }

 

  添加键盘按钮事件

    private void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S))
        {
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A))
        {
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D))
        {
            x = step;y = 0;
        }
    }

  

  初始时及改变方向时不断让小蛇向着一个方向移动

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = step;y = 0;
    }

 

  

Invoke() 方法是 Unity3D 的一种委托机制

如: Invoke("Test", 5);   它的意思是:5 秒之后调用 Test() 方法;

使用 Invoke() 方法需要注意 3点:

1 :它应该在 脚本的生命周期里的(Start、Update、OnGUI、FixedUpdate、LateUpdate)中被调用;

2:Invoke(); 不能接受含有参数的方法;

3:在 Time.ScaleTime = 0; 时, Invoke() 无效,因为它不会被调用到

Invoke() 也支持重复调用:InvokeRepeating("Test", 2 , 3); 

这个方法的意思是指:2 秒后调用 Test() 方法,并且之后每隔 3 秒调用一次 Test() 方法

还有两个重要的方法:

IsInvoking:用来判断某方法是否被延时,即将执行
CancelInvoke:取消该脚本上的所有延时方法
Unity中Invoke 和 InvokeRepeating的区别

 

 

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

public class SnakeHead : MonoBehaviour {

    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = step;y = 0;
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S))
        {
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A))
        {
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D))
        {
            x = step;y = 0;
        }
    }

    void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
    }
}
SnakeHead.cs

 

  给小蛇头部添加转动时改变头部方向动画

 private void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

 

 

  发现小球向左走时还能向右走,向下走时还能向上走

  判断按键方法时候可以对方向进行一个判断

    private void Update()
    {
        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

 

  添加按下空格加快贪吃蛇向前移动速度

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.2f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

 

  Unity圣典  传送门

  MonoBehaviour.CancelInvoke 取消调用

     MonoBehaviour.InvokeRepeating 重复调用

 

 

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

public class SnakeHead : MonoBehaviour {

    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.2f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
    }
}
SnakeHead.cs

 

 

 

第一个食物的随机生成

 

  对游戏边界范围的判定

  上边界与下边界

 

  经测试

  小蛇走11步碰到上边界,走10步碰到下边界

  小蛇走19步碰到右边界,走11步碰到左边界

 

  将食物和小蛇设置为预制体

 

  创建一个GameObject空物体对象挂在脚本和预制体

 

  游戏初始时获得游戏物体,并且生成食物

 private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood();
    }

  

  随机生成食物的位置

 public int xlimit = 18;
 public int ylimit = 10;
 //x轴活动空间不对称问题,对x轴的偏移值
 public int xoffset = 11;    

 void MakeFood()
 {
   //生成x和y的随机值
   int x = Random.Range(-xlimit + xoffset,xlimit);
   int y = Random.Range(-ylimit,ylimit);
   //通过小蛇自身的步数长度来计算食物的活动空间
   food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
 }

 

  随机生成食物的种类和位置

 void MakeFood()
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        //通过小蛇自身的步数长度来计算食物的活动空间
        food.transform.localPosition = new Vector3(x * 25, y * 25, 0);
    }

 

 

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

public class FoodMaker : MonoBehaviour {

    public int xlimit = 18;
    public int ylimit = 10;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 11;
    public GameObject foodPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood();
    }

    void MakeFood()
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        //通过小蛇自身的步数长度来计算食物的活动空间
        food.transform.localPosition = new Vector3(x * 25, y * 25, 0);
    }
}
FoodMaker.cs

 

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

public class SnakeHead : MonoBehaviour {

    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.2f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
    }
}
SnakeHead.cs

 

 

吃掉食物(销毁)以及食物的随机生成

 

  给蛇头添加Box Collider 2D碰撞器,为避免蛇头未碰撞食物时碰撞器就碰撞到食物,可以设置碰撞器范围比蛇头小一圈

  勾选Is Trigger,当物体碰到食物及边界时,可以进入边界再判定游戏结束

 

  给蛇头添加Rigidbody 2D碰撞器,设置重力 Gravity Scale为0  (不然蛇头在开始游戏时就会不断往下掉)

 

  同理,给食物预设体Food添加Box Collider 2D碰撞器

 

  新创建一个Food标签作为标记

 

  将Food预设体标签设置为Food

 

  SnakeHead.cs中添加食物碰撞生成新食物方法

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            FoodMaker.Instance.MakeFood();
        }
    }

 

  FoodMaker.cs

 

  添加一个单例模式

 

    //单例模式
    private static FoodMaker _instance;
    //可以通过外部去调用方法修改_instance的值
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }

    //初始化
    private void Awake()
    {
        _instance = this;
    }

 

 

 

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 17;
    public int ylimit = 8;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 11;
    public GameObject foodPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood();
    }

    public void MakeFood()
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        //通过小蛇自身的步数长度来计算食物的活动空间
        food.transform.localPosition = new Vector3(x * 25, y * 25, 0);
    }
}
FoodMaker.cs

 

 

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

public class SnakeHead : MonoBehaviour {

    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.2f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            FoodMaker.Instance.MakeFood();
        }
    }

}
SnakeHead.cs

 

 

 

蛇身吃掉食物的变长

 

  添加一个Image放置蛇身图片,添加Box Collider 2D碰撞器(碰撞器的范围不用太大,以免对食物产生不可描述的误操作)

 

  SnakeHead.cs脚本中生成蛇身

  创建一个集合,用来保存蛇身部分(两张图片不断轮流交换)

 public List<RectTransform> bodyList = new List<RectTransform>();


 public GameObject bodyPrefab;
 public Sprite[] bodySprites = new Sprite[2];

 

  生成蛇身体方法

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas,false);
        bodyList.Add(body.transform);
    }

 

 

 

蛇生体移动的方法

 

   创建一个链表,当蛇吃了食物后,将蛇身放置到链表中

 public List<Transform> bodyList = new List<Transform>();

 

  将图片放置到bodySprites中(两张),存放的都是bodyPrefab

    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];

    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }

 

  Unity中将预制体与贪吃蛇头部绑定一下

 

   蛇碰到食物后开始生成蛇身

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

 

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            Grow();
            FoodMaker.Instance.MakeFood();
        }
    }

 

 

  

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood();
    }

    public void MakeFood()
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
    }
}
FoodMaker.cs

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;

    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];

    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            Grow();
            FoodMaker.Instance.MakeFood();
        }
    }

}
SnakeHead.cs

 

 

游戏的自由模式

 

  当小蛇撞到上边界时再往下走一步后从下边界出现

  当小蛇撞到下边界时再往上走一步后从上边界出现

  当小蛇撞到上边界时再往下走一步后从下边界出现

  当小蛇撞到上边界时再往下走一步后从下边界出现

 

     switch(collision.gameObject.name)
            {
                case "Up":
                    transform.localPosition = new Vector3(transform.localPosition.x,-transform.localPosition.y+20,transform.localPosition.z);
                    break;
                case "Down":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y-20, transform.localPosition.z);
                    break;
                case "Left":
                    transform.localPosition = new Vector3(-transform.localPosition.x+140,transform.localPosition.y , transform.localPosition.z);
                    break;
                case "Right":
                    transform.localPosition = new Vector3(-transform.localPosition.x+170, transform.localPosition.y, transform.localPosition.z);
                    break;
            }

 

 

 

奖励目标的生成

 

  添加一个Reward(Image)作为奖励目标背景图片

 

  调整奖励背景图片和食物大小保持一样,并添加Box Collider 2D组件

  

  将Resward作为预制体

  FoodMaker.cs上绑定rewardPrefabs预制体

    public GameObject rewardPrefab;

 

  

 

   判断是否生成道具

  public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }

 

   当蛇吃到食物后,有百分之20的机率生成道具

 if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }

 

  当蛇吃到道具时,道具销毁

 else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            Grow();
        }

 

  

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;

    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];



    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            Grow();
        }
        else if(collision.gameObject.CompareTag("Body"))
        {
            Debug.Log("Die");
        }
        else
        {
            switch(collision.gameObject.name)
            {
                case "Up":
                    transform.localPosition = new Vector3(transform.localPosition.x,-transform.localPosition.y+20,transform.localPosition.z);
                    break;
                case "Down":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y-20, transform.localPosition.z);
                    break;
                case "Left":
                    transform.localPosition = new Vector3(-transform.localPosition.x+140,transform.localPosition.y , transform.localPosition.z);
                    break;
                case "Right":
                    transform.localPosition = new Vector3(-transform.localPosition.x+170, transform.localPosition.y, transform.localPosition.z);
                    break;
            }
        }
    }

}
SnakeHead.cs

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public GameObject rewardPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }

    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }
}
FoodMaker.cs

 

 

分数与长度与游戏背景切换

 

  新建一个脚本MainUIController.cs来存储蛇的分数与长度

 

  分数的单例模式

 //单例模式
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

  

  游戏加分方法  默认吃到一个食物加5分1个长度  获得道具加分会更高

public void UpdateUI(int s = 5,int l = 1)
    {
        score += s;
        length += l;
        scoreText.text ="得分:\n"+score;
        lengthText.text = "长度\n" + length;
    }

 

  当蛇吃到食物或道具时调用UpdateUI()方法

 if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);
            Grow();
        }

 

  当分数达到一定数值时能进行游戏背景变色

    private void Update()
    {
        switch (score / 100)
        {
            case 3:
                ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);
                bgImage.color=tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            case 11:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段 x";
                break;
        }
    }

 

  将脚本放在Script游戏物体上,并绑定分数版、背景等

 

 

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;

    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];



    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step  )
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);
            Grow();
        }
        else if(collision.gameObject.CompareTag("Body"))
        {
            Debug.Log("Die");
        }
        else
        {
            switch(collision.gameObject.name)
            {
                case "Up":
                    transform.localPosition = new Vector3(transform.localPosition.x,-transform.localPosition.y+20,transform.localPosition.z);
                    break;
                case "Down":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y-20, transform.localPosition.z);
                    break;
                case "Left":
                    transform.localPosition = new Vector3(-transform.localPosition.x+140,transform.localPosition.y , transform.localPosition.z);
                    break;
                case "Right":
                    transform.localPosition = new Vector3(-transform.localPosition.x+170, transform.localPosition.y, transform.localPosition.z);
                    break;
            }
        }
    }

}
SnakeHead.cs

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public GameObject rewardPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }

    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }
}
FoodMaker.cs

 

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

public class MainUIController : MonoBehaviour {

    //单例模式
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;
    public Image bgImage;
    private Color tempColor;

    void Awake()
    {
        _instance = this;    
    }

    private void Update()
    {
        switch (score / 100)
        {
            case 3:
                ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);
                bgImage.color=tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            case 11:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段 x";
                break;
        }
    }

    public void UpdateUI(int s = 5,int l = 1)
    {
        score += s;
        length += l;
        scoreText.text ="得分:\n"+score;
        lengthText.text = "长度\n" + length;
    }

}
MainUIController.cs

 

 

暂停与返回按钮

 

  对按钮进行引用以及添加一个图片Sprite[]

    public Button pauseButton;
    public Sprite[] pauseSprites;

 

  设置一个变量记录游戏状态

    private bool isPause = false ;

 

  点击按钮时候对游戏状态取反

    public void Pause()
    {
        isPause = !isPause;
        if(isPause)
        {
            Time.timeScale = 0;
            pauseButton.GetComponent<Image>().sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseButton.GetComponent<Image>().sprite = pauseSprites[0];
        }
    }

 

  Time.timeScale  传送门

  Script中对图片、按钮进行绑定

  

  

  在onclick()

 

 

 

  解决按键冲突

  按键控制器Edit->Project Settings-> Input 

    

  

 private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false)
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space) && MainUIController.Instance.isPause == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }
Update()中对键盘按下事件进行处理

 

   接下来实现返回按钮

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }

 

  Home按钮上添加点击事件

 

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;

    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];



    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false)
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space) && MainUIController.Instance.isPause == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step && MainUIController.Instance.isPause == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);
            Grow();
        }
        else if(collision.gameObject.CompareTag("Body"))
        {
            Debug.Log("Die");
        }
        else
        {
            switch(collision.gameObject.name)
            {
                case "Up":
                    transform.localPosition = new Vector3(transform.localPosition.x,-transform.localPosition.y+20,transform.localPosition.z);
                    break;
                case "Down":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y-20, transform.localPosition.z);
                    break;
                case "Left":
                    transform.localPosition = new Vector3(-transform.localPosition.x+140,transform.localPosition.y , transform.localPosition.z);
                    break;
                case "Right":
                    transform.localPosition = new Vector3(-transform.localPosition.x+170, transform.localPosition.y, transform.localPosition.z);
                    break;
            }
        }
    }

}
SnakeHead.cs

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public GameObject rewardPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }

    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
}
FoodMaker.cs

 

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

public class MainUIController : MonoBehaviour {

    //单例模式
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;
    public Image bgImage;
    private Color tempColor;

    public Image pauseImage;
    public Sprite[] pauseSprites;
    public bool isPause = false ;

    void Awake()
    {
        _instance = this;    
    }

    private void Update()
    {
        switch (score / 100)
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);
                bgImage.color=tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            case 11:
            case 12:
            case 13:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段 x";
                break;
        }
    }

    public void UpdateUI(int s = 5,int l = 1)
    {
        score += s;
        length += l;
        scoreText.text ="得分:\n"+score;
        lengthText.text = "长度\n" + length;
    }

    public void Pause()
    {
        isPause = !isPause;
        if(isPause)
        {
            Time.timeScale = 0;
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseImage.sprite = pauseSprites[0];
        }
    }
}
MainUIController.cs

 

 

游戏贪吃蛇死亡处理

 

  添加游戏死亡标记

    private bool isDie = false;

 

  判断游戏死亡时触发的粒子特效

    void Die()
    {
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
        StartCoroutine(GameOver(1.0f));
    }

 

  通过Unity协成进行游戏的重新开始

    IEnumerator GameOver(float t)
    {
        yield return new WaitForSeconds(t);
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }

 

  

 

  游戏结束时候记录最高得分

    void Die()
    {
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
        //记录游戏的最后长度
        PlayerPrefs.SetInt("last1",MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
        //当游戏长度大于最高得分时
        if (PlayerPrefs.GetInt("bests", 0)<MainUIController.Instance.score)
        {
            //将当前游戏长度和分数记录到best1和bests当中
            PlayerPrefs.SetInt("best1", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }
        StartCoroutine(GameOver(1.0f));
    }

 

1、存储值://本地化存储方式,一般用在保存玩家的偏好设置中,常见于玩家设置,游戏分数存取…………

  PlayerPrefs.SetFloat(string key,float value); //通过key 与value 存储,就像键值对一样。

  PlayerPrefs.SetInt(string key,Int value);

  PlayerPrefs.SetString(string key,string value);

2、读取值:

  PlayerPrefs.GetFloat(string key);  //通过key得到存储的value值

  PlayerPrefs.GetInt(string key);

  PlayerPrefs.GetString(string key);
Unity 中PlayerPrefs类实现数据本地化存储

 

 

用户设置的存储

 

  添加StartUIController.cs脚本控制Gary开始场景界面

  获得Gary开始场景界面Text文本控件

    public Text lastText;
    public Text bestText;

 

  对文本控件的值进行读取刷新

    private void Awake()
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("last1",0)  + ",分数" + PlayerPrefs.GetInt("lasts",0);
        lastText.text = "最好:长度" + PlayerPrefs.GetInt("best1", 0) + ",分数" + PlayerPrefs.GetInt("bests", 0);
    }

 

  Gary场景中创建一个空物体游戏对象ScriptHolder挂在游戏脚本

  

 

  实现点击开始进入场景功能

  切换场景方法

    public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }

 

  Gary场景中Start添加onClick()点击事件

 

(可以看到此时已经实现上次分数以及最高分数的存储)

 

  动态绑定选择游戏皮肤、模式

  存储玩家的选择

 private void Start()
    {
        if (PlayerPrefs.GetString("sh", "sh01") == "sh01")
        {
            blue.isOn = true;
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true;
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border",1);
        }
        else
        {
            noborder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void BlueSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }
       
    public void YellowSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

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

    public void NoBorderSelected(bool isOn)
    {
        if (isOn)
        {
            //自由模式
            PlayerPrefs.SetInt("border", 0);
        }
    }

 

 

 

 

完成换肤与数据读取

 

  使用泛型加载游戏换肤方法

    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
        //通过Resources.Load(string path)方法加载资源;
        gameObject.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh", "sh02"));
        bodySprites[0]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sh01", "sh0201"));
        bodySprites[1]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sh02", "sh0202"));
    }

 

 

  判断游戏模式

  添加一个模式表示位

    public bool hasBorder = true;

 

  游戏初始时默认无边界

 void Start()
    {
        if (PlayerPrefs.GetInt("border", 1)==0)
        {
            hasBorder = false;
            //取消边界上的颜色
            foreach(Transform t in bgImage.gameObject.transform)
            {
                t.gameObject.GetComponent<Image>().enabled = false;
            }
        }
    }

 

  SnakeHead.cs脚本中对游戏碰到边界死亡进行判定

if (MainUIController.Instance.hasBorder)
            {
                Die();
            }
            else
            {
                switch (collision.gameObject.name)
                {
                    case "Up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 20, transform.localPosition.z);
                        break;
                    case "Down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 20, transform.localPosition.z);
                        break;
                    case "Left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 140, transform.localPosition.y, transform.localPosition.z);
                        break;
                    case "Right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 170, transform.localPosition.y, transform.localPosition.z);
                        break;
                }
            }

 

 

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

public class StartUIController : MonoBehaviour {

    public Text lastText;
    public Text bestText;
    public Toggle blue;
    public Toggle yellow;
    public Toggle border;
    public Toggle noborder;

    private void Awake()
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("last1",0)  + ",分数" + PlayerPrefs.GetInt("lasts",0);
        bestText.text = "最好:长度" + PlayerPrefs.GetInt("best1", 0) + ",分数" + PlayerPrefs.GetInt("bests",0);
    }

    private void Start()
    {
        if (PlayerPrefs.GetString("sh", "sh01") == "sh01")
        {
            blue.isOn = true;
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true;
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border",1);
        }
        else
        {
            noborder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void BlueSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }
       
    public void YellowSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

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

    public void NoBorderSelected(bool isOn)
    {
        if (isOn)
        {
            //自由模式
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
}
StartUIController.cs

 

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

public class MainUIController : MonoBehaviour {

    //单例模式
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;
    public Image bgImage;
    private Color tempColor;

    public Image pauseImage;
    public Sprite[] pauseSprites;
    public bool isPause = false ;
    public bool hasBorder = true;

    void Awake()
    {
        _instance = this;    
    }

    void Start()
    {
        if (PlayerPrefs.GetInt("border", 1)==0)
        {
            hasBorder = false;
            //取消边界上的颜色
            foreach(Transform t in bgImage.gameObject.transform)
            {
                t.gameObject.GetComponent<Image>().enabled = false;
            }
        }
    }

    private void Update()
    {
        switch (score / 100)
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);
                bgImage.color=tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            case 11:
            case 12:
            case 13:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段 x";
                break;
        }
    }

    public void UpdateUI(int s = 5,int l = 1)
    {
        score += s;
        length += l;
        scoreText.text ="得分:\n"+score;
        lengthText.text = "长度\n" + length;
    }

    public void Pause()
    {
        isPause = !isPause;
        if(isPause)
        {
            Time.timeScale = 0;
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseImage.sprite = pauseSprites[0];
        }
    }
}
MainUIController.cs

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public GameObject rewardPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }

    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
}
FoodMaker.cs

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;
    private bool isDie = false;

    public GameObject dieEffect;
    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];



    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
        //通过Resources.Load(string path)方法加载资源;
        gameObject.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh", "sh02"));
        bodySprites[0]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01", "sb0201"));
        bodySprites[1]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02", "sb0202"));
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false&&isDie==false)
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space) && MainUIController.Instance.isPause == false && isDie == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    void Die()
    {
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
        //记录游戏的最后长度
        PlayerPrefs.SetInt("last1",MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
        //当游戏长度大于最高得分时
        if (PlayerPrefs.GetInt("bests", 0)<MainUIController.Instance.score)
        {
            //将当前游戏长度和分数记录到best1和bests当中
            PlayerPrefs.SetInt("best1", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }
        StartCoroutine(GameOver(1.0f));
    }

    IEnumerator GameOver(float t)
    {
        yield return new WaitForSeconds(t);
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);
            Grow();
        }
        else if(collision.gameObject.CompareTag("Body"))
        {
            Die();
        }
        else
        {
            if (MainUIController.Instance.hasBorder)
            {
                Die();
            }
            else
            {
                switch (collision.gameObject.name)
                {
                    case "Up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 20, transform.localPosition.z);
                        break;
                    case "Down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 20, transform.localPosition.z);
                        break;
                    case "Left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 140, transform.localPosition.y, transform.localPosition.z);
                        break;
                    case "Right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 170, transform.localPosition.y, transform.localPosition.z);
                        break;
                }
            }
        }
    }

}
SnakeHead.cs

 

 

添加游戏音乐

 

  Main场景摄像机上绑定一个Audio Source音乐播放器

 

  吃到东西和游戏结束时分别播放两个不同的音乐

    public AudioClip eatClip;
    public AudioClip dieClip;

 

  

void Grow()
    {
        //播放贪吃蛇变长音乐
        AudioSource.PlayClipAtPoint(eatClip,Vector3.zero);
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    void Die()
    {
        //播放死亡音乐
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero);
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
        //记录游戏的最后长度
        PlayerPrefs.SetInt("last1",MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
        //当游戏长度大于最高得分时
        if (PlayerPrefs.GetInt("bests", 0)<MainUIController.Instance.score)
        {
            //将当前游戏长度和分数记录到best1和bests当中
            PlayerPrefs.SetInt("best1", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }
        StartCoroutine(GameOver(1.0f));
    }

 

  

 

 

 

游戏源代码

 

  控制蛇和食物脚本

 

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

public class SnakeHead : MonoBehaviour {

    public List<Transform> bodyList = new List<Transform>();
    public float velocity=0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;
    private Transform canvas;
    private bool isDie = false;

    public AudioClip eatClip;
    public AudioClip dieClip;
    public GameObject dieEffect;
    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];



    private void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
        //通过Resources.Load(string path)方法加载资源;
        gameObject.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh", "sh02"));
        bodySprites[0]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01", "sb0201"));
        bodySprites[1]  = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02", "sb0202"));
    }

    private void Start()
    {
        //重复调用
        InvokeRepeating("Move",0,velocity);
        x = 0;y = step;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false&&isDie==false)
        {
            CancelInvoke();
            InvokeRepeating("Move",0,velocity - 0.3f);
        }

        if (Input.GetKeyUp(KeyCode.Space) && MainUIController.Instance.isPause == false && isDie == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }

        if (Input.GetKey(KeyCode.W) &&  y!=-step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
        }
        if (Input.GetKey(KeyCode.S) &&  y!=step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && x!=step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step;y = 0;
        }
        if (Input.GetKey(KeyCode.D) &&  x!=-step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step;y = 0;
        }
    }

    void Move()
    {
        //保存下来蛇头移动前的位置
        headPos = gameObject.transform.localPosition;
        //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x+x,headPos.y+y,headPos.z);
        if (bodyList.Count > 0)
        {
            //由于是双色蛇身,此方法弃用
            //将蛇尾移动到蛇头移动前的位置
           // bodyList.Last().localPosition = headPos;
           //将蛇尾在List中的位置跟新到最前
           // bodyList.Insert(0, bodyList.Last());
           //溢出List最末尾的蛇尾引用
           // bodyList.RemoveAt(bodyList.Count - 1);

            //从后面开始移动蛇身
            for(int i =bodyList.Count-2;i>=0 ;i--)
            {
                //每一个蛇身都移动到它前面一个
                bodyList[i + 1].localPosition = bodyList[i].localPosition;
            }
            //第一个蛇身移动到蛇头移动前的位置
            bodyList[0].localPosition = headPos;

        }
    }

    void Grow()
    {
        //播放贪吃蛇变长音乐
        AudioSource.PlayClipAtPoint(eatClip,Vector3.zero);
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab,new Vector3(200000,2000000,0),Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

    void Die()
    {
        //播放死亡音乐
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero);
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
        //记录游戏的最后长度
        PlayerPrefs.SetInt("last1",MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
        //当游戏长度大于最高得分时
        if (PlayerPrefs.GetInt("bests", 0)<MainUIController.Instance.score)
        {
            //将当前游戏长度和分数记录到best1和bests当中
            PlayerPrefs.SetInt("best1", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }
        StartCoroutine(GameOver(1.0f));
    }

    IEnumerator GameOver(float t)
    {
        yield return new WaitForSeconds(t);
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            if (Random.Range(0,100)<20)
            {
                FoodMaker.Instance.MakeFood(true);
            }
            else
            {
                FoodMaker.Instance.MakeFood(false);
            }
        }
        else if(collision.gameObject.CompareTag("Reward"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);
            Grow();
        }
        else if(collision.gameObject.CompareTag("Body"))
        {
            Die();
        }
        else
        {
            if (MainUIController.Instance.hasBorder)
            {
                Die();
            }
            else
            {
                switch (collision.gameObject.name)
                {
                    case "Up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 20, transform.localPosition.z);
                        break;
                    case "Down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 20, transform.localPosition.z);
                        break;
                    case "Left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 140, transform.localPosition.y, transform.localPosition.z);
                        break;
                    case "Right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 170, transform.localPosition.y, transform.localPosition.z);
                        break;
                }
            }
        }
    }

}
SnakeHead.cs

 

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

public class FoodMaker : MonoBehaviour {

    //单例模式
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
    public int xlimit = 18;
    public int ylimit = 9;
    //x轴活动空间不对称问题,对x轴的偏移值
    public int xoffset = 10;
    public GameObject foodPrefab;
    public GameObject rewardPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }

    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0,foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder,false);
        int x = Random.Range(-xlimit + xoffset,xlimit);
        int y = Random.Range(-ylimit,ylimit);
        food.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        //food.transform.localPosition = new Vector3( x,  y, 0);
        if(isReward==true)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 20, y * 20, 0);
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
}
FoodMaker.cs

 

 

  场景UI控制脚本 

 

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

public class MainUIController : MonoBehaviour {

    //单例模式
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;
    public Image bgImage;
    private Color tempColor;

    public Image pauseImage;
    public Sprite[] pauseSprites;
    public bool isPause = false ;
    public bool hasBorder = true;

    void Awake()
    {
        _instance = this;    
    }

    void Start()
    {
        if (PlayerPrefs.GetInt("border", 1)==0)
        {
            hasBorder = false;
            //取消边界上的颜色
            foreach(Transform t in bgImage.gameObject.transform)
            {
                t.gameObject.GetComponent<Image>().enabled = false;
            }
        }
    }

    private void Update()
    {
        switch (score / 100)
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);
                bgImage.color=tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            case 11:
            case 12:
            case 13:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段 x";
                break;
        }
    }

    public void UpdateUI(int s = 5,int l = 1)
    {
        score += s;
        length += l;
        scoreText.text ="得分:\n"+score;
        lengthText.text = "长度\n" + length;
    }

    public void Pause()
    {
        isPause = !isPause;
        if(isPause)
        {
            Time.timeScale = 0;
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseImage.sprite = pauseSprites[0];
        }
    }
}
MainUIController.cs

 

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

public class StartUIController : MonoBehaviour {

    public Text lastText;
    public Text bestText;
    public Toggle blue;
    public Toggle yellow;
    public Toggle border;
    public Toggle noborder;

    private void Awake()
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("last1",0)  + ",分数" + PlayerPrefs.GetInt("lasts",0);
        bestText.text = "最好:长度" + PlayerPrefs.GetInt("best1", 0) + ",分数" + PlayerPrefs.GetInt("bests",0);
    }

    private void Start()
    {
        if (PlayerPrefs.GetString("sh", "sh01") == "sh01")
        {
            blue.isOn = true;
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true;
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border",1);
        }
        else
        {
            noborder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void BlueSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }
       
    public void YellowSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

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

    public void NoBorderSelected(bool isOn)
    {
        if (isOn)
        {
            //自由模式
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
}
StartUIController.cs

 

转载于:https://www.cnblogs.com/1138720556Gary/p/9733959.html

2019-09-05 09:31:26 q764424567 阅读数 204

一、前言

让我们在团结中制作一个电子风格的2D游戏。两名球员将能够相互竞争。目标是移动你的光线以一种诱捕另一个玩家的方式,有点像一个多人贪吃蛇游戏。

我们的游戏将非常简单,只有不到60行代码和只有三个资产。

像往常一样,一切都会尽可能简单地解释,这样每个人都能理解它。

以下是最后游戏的预览:在这里插入图片描述

二、项目说明

版本:Unity5.0.0f4

三、正文

1、摄像机设置

在这里插入图片描述

2、背景图像

一个普通的黑色背景是相当无聊的,所以让我们使用我们选择的绘图工具来绘制一些我们可以用于我们的背景的网格图像:
在这里插入图片描述
将图片拖入到我们的项目中Sprites文件夹中:
在这里插入图片描述
修改设置:
在这里插入图片描述
注:Pixels Per Unit 2这意味着2x2像素将适合在游戏世界的一个单位。我们将使用这个值的所有纹理,因为Player Sprites将有大小为2x2像素后。其他设置只是视觉效果。我们想要使图像看起来非常清晰,没有任何压缩。

拖入到场景中:
在这里插入图片描述
注意:重新调整位置(0, 0, 0).

调整层级:
在这里插入图片描述
注意:通常我们会创建一个全新的Background Sorting Layer,但对于这样一个简单的游戏,则使用Order in Layer就足够了。

2、灯饰

玩家应该在他们移动的任何地方留下一个光环,所以让我们创建第一个。

青色灯笼:

我们将首先绘制一个仅由青色组成的2x2px图像:
在这里插入图片描述
在这里插入图片描述

导入设置:
在这里插入图片描述

拖到场景中:
在这里插入图片描述
添加物理组件:
在这里插入图片描述
制作预制体:
在这里插入图片描述
粉红灯笼:
让我们重复上面的工作流程,以获得粉色灯塔图像:

在这里插入图片描述
在这里插入图片描述
所以我们最终得到了另一个预制件:
在这里插入图片描述

3、玩家

现在是加入玩家的时候了。玩家应该是一个简单的白色方块,可以通过按一些键移动。玩家也会拖着灯笼到任何他去的地方。

让我们为玩家画一个白色的2x2px图像:
在这里插入图片描述
在这里插入图片描述
导入设置:
在这里插入图片描述
拖入场景中:
在这里插入图片描述
修改层级:
在这里插入图片描述
注意:正如前面提到的,我们通常会使用排序层。然而,由于我们的游戏中只有三个元素:背景、玩家和灯光,所以我们将保持简单,只使用三个不同的元素层序价值。

Player添加物理组件:
在这里插入图片描述
注意:我们启用了IsTrigger以避免与玩家自己的“光环”发生碰撞。只要我们有IsTrigger启用后,玩家将只接收碰撞信息,而不会实际与任何东西发生冲突。这很快就有意义了。

添加刚体:
在这里插入图片描述
添加玩家移动脚本Move.cs:

using UnityEngine;
using System.Collections;

public class Move : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

首先,我们想知道是否按下了移动键。现在我们只想为两个玩家创建一个移动脚本,所以让我们使移动键可自定义,这样我们就可以使用箭一个玩家的键和WSAD另一个玩家的钥匙:

using UnityEngine;
using System.Collections;

public class Move : MonoBehaviour {
    // Movement keys (customizable in Inspector)
    public KeyCode upKey;
    public KeyCode downKey;
    public KeyCode rightKey;
    public KeyCode leftKey;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

如果我们保存脚本并查看Inspector然后,我们可以将关键变量设置为Arrow Keys:
在这里插入图片描述
好的,让我们检查一下我们的按键更新职能:

// Update is called once per frame
void Update () {
    // Check for key presses
    if (Input.GetKeyDown(upKey)) {
        // Do stuff...
    }
    else if (Input.GetKeyDown(downKey)) {
        // Do stuff...
    }
    else if (Input.GetKeyDown(rightKey)) {
        // Do stuff...
    }
    else if (Input.GetKeyDown(leftKey)) {
        // Do stuff...
    }
}

现在,只要玩家按下其中的任何一个键,我们就想让玩家移动到那个方向。如前所述,我们将使用刚体的速度那个的财产。速度总是运动方向乘以运动速度…让我们首先添加一个运动速度变量:

using UnityEngine;
using System.Collections;

public class Move : MonoBehaviour {
    // Movement keys (customizable in Inspector)
    public KeyCode upKey;
    public KeyCode downKey;
    public KeyCode rightKey;
    public KeyCode leftKey;

    // Movement Speed
    public float speed = 16;

    ...
}

剩下的就很简单了。我们要做的就是修改我们的更新函数再一次设置刚体的velocity 属性:

// Update is called once per frame
void Update () {
    // Check for key presses
    if (Input.GetKeyDown(upKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.up * speed;
    }
    else if (Input.GetKeyDown(downKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.up * speed;
    }
    else if (Input.GetKeyDown(rightKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.right * speed;
    }
    else if (Input.GetKeyDown(leftKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.right * speed;
    }
}

注:-向量2.向上手段降下来和-向量2.右手段左边.

让我们也修改一下我们的启动函数非常快地给玩家一个初始速度:

// Use this for initialization
void Start () {
    // Initial Velocity
    GetComponent<Rigidbody2D>().velocity = Vector2.up * speed;
}

然后我们点击Play,用箭头可以移动我们的Player:
在这里插入图片描述
Player灯饰
我们想要添加一个功能,以创建一个灯塔,无论球员去哪里。我们真正需要做的就是在玩家转变成一个新的方向后,尽快创建一个新的“光环”,然后在玩家进入另一个方向之前,始终在灯光中心进行缩放。

我们将需要一个辅助功能,产生一个新的照明大楼。首先,我们将向脚本中添加两个变量。其中一个是闪电城预制板,另一个是目前被玩家拖着的墙:

public class Move : MonoBehaviour {
    // Movement keys (customizable in Inspector)
    public KeyCode upKey;
    public KeyCode downKey;
    public KeyCode rightKey;
    public KeyCode leftKey;

    // Movement Speed
    public float speed = 16;

    // Wall Prefab
    public GameObject wallPrefab;

    // Current Wall
    Collider2D wall;

    ...

现在我们可以用实例化若要创建在玩家当前位置生成新的“光环”的函数,请执行以下操作:

void spawnWall() {
    // Spawn a new Lightwall
    GameObject g = (GameObject)Instantiate(wallPrefab, transform.position, Quaternion.identity);
    wall = g.GetComponent<Collider2D>();
}

注:变换位置是玩家目前的位置四元数身份是默认的旋转。我们还保存了游戏对象Collider2D在我们墙变量来跟踪当前墙。

让我们保存脚本,然后拖动淡青色预制件项目区在剧本里壁板插槽:
在这里插入图片描述
好了,是时候利用我们的助手函数了。现在我们将修改脚本的更新功能在改变方向后生成一个新的“光塔”:

// Update is called once per frame
void Update () {
    // Check for key presses
    if (Input.GetKeyDown(upKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.up * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(downKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.up * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(rightKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.right * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(leftKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.right * speed;
        spawnWall();
    }
}

当游戏开始时,我们还会孕育出一个新的“光塔”:

// Use this for initialization
void Start () {
    // Initial Velocity
    GetComponent<Rigidbody2D>().velocity = Vector2.up * speed;
    spawnWall();
}

如果我们保存脚本并按下Player,然后我们可以看到,在每个方向改变之后,一个新的光塔是如何产生的:
在这里插入图片描述
到现在为止还好。

现在灯台只是个小方块,我们还得把它们放大。让我们创建一个新的适口函数,它接受对撞机和两点,然后将对撞机放在这两个点之间:

void fitColliderBetween(Collider2D co, Vector2 a, Vector2 b) {
    // Calculate the Center Position
    co.transform.position = a + (b - a) * 0.5f;

    // Scale it (horizontally or vertically)
    float dist = Vector2.Distance(a, b);
    if (a.x != b.x)
        co.transform.localScale = new Vector2(dist, 1);
    else
        co.transform.localScale = new Vector2(1, dist);
}

*注意:这个函数一开始看起来有点混乱。在两点之间安装对撞机的明显方法是对撞机.setMinMax()但团结组织不允许这样做。相反,我们将简单地使用变换位置属性将其精确定位在两个点之间,然后使用地区规模属性使其变得非常长,因此它正好适合于点之间的位置。公式A+(b-a)0.5也很容易理解。首先,我们从a到b用(b-a)…然后我们简单地把这个方向的一半加到这个点上。a,这就产生了中心点。之后,我们通过比较这两条线,找出这条线应该是水平的还是垂直的。x坐标。如果它们相等,则这条线是水平的,否则是垂直的。最后,我们调整比例,使墙是区单位长和1单位范围。

让我们利用我们的适口功能。我们总是想要适应对撞机之间的最后一个对撞机结束和球员的当前位置。所以首先,我们必须跟踪最后一架对撞机的尾声。

我们将添加一个lastWallEnd变量到我们的脚本中:

public class Move : MonoBehaviour {
    // Movement keys (customizable in Inspector)
    public KeyCode upKey;
    public KeyCode downKey;
    public KeyCode rightKey;
    public KeyCode leftKey;

    // Movement Speed
    public float speed = 16;

    // Wall Prefab
    public GameObject wallPrefab;

    // Current Wall
    Collider2D wall;

    // Last Wall's End
    Vector2 lastWallEnd;

    ...

并在我们的spawnWall函数:

void spawnWall() {
    // Save last wall's position
    lastWallEnd = transform.position;

    // Spawn a new Lightwall
    GameObject g = (GameObject)Instantiate(wallPrefab, transform.position, Quaternion.identity);
    wall = g.GetComponent<Collider2D>();
}

注意:从技术上讲,最后一道墙的位置应该是变位,但我们用了玩家的变换位置这里。原因是在第一堵墙产卵的时候,还没有最后一堵墙,所以我们不能设置最后沃伦德就位。相反,我们总是把它设置在球员的当前位置,然后再打下一道墙,结果几乎是一样的。

快好了。现在我们可以修改我们的更新再次作用,以始终适合当前墙之间的最后一面墙的结束位置和球员的当前位置:

// Update is called once per frame
void Update () {
    // Check for key presses
    if (Input.GetKeyDown(upKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.up * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(downKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.up * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(rightKey)) {
        GetComponent<Rigidbody2D>().velocity = Vector2.right * speed;
        spawnWall();
    }
    else if (Input.GetKeyDown(leftKey)) {
        GetComponent<Rigidbody2D>().velocity = -Vector2.right * speed;
        spawnWall();
    }

    fitColliderBetween(wall, lastWallEnd, transform.position);
}

如果我们保存脚本并按下Player,然后我们就可以看到在播放器后面是如何创建的:
在这里插入图片描述
如果我们仔细看一看,我们就能看到墙角上的墙少了一点:
在这里插入图片描述
我们的问题有一个很容易解决的办法。我们要做的就是回到我们的适口功能和始终使墙壁一个单位长:

void fitColliderBetween(Collider2D co, Vector2 a, Vector2 b) {
    // Calculate the Center Position
    co.transform.position = a + (b - a) * 0.5f;

    // Scale it (horizontally or vertically)
    float dist = Vector2.Distance(a, b);
    if (a.x != b.x)
        co.transform.localScale = new Vector2(dist + 1, 1);
    else
        co.transform.localScale = new Vector2(1, dist + 1);
}

如果我们保存脚本并按下Play,踢然后我们可以看到一些完全匹配的角:
在这里插入图片描述

4、加入另一个玩家

点击玩家,右键点击Duplicate复制一个对象,或者Ctrl+C Ctrl+D也行:
在这里插入图片描述
我们将把复制的Player重命名为player_pink并将其坐标改为(-3, 0, 0):
在这里插入图片描述
我们还可以将移动键更改为WSAD并拖动淡粉色预制件墙体预制件插槽:
在这里插入图片描述
如果我们按下Play,然后,我们现在可以控制两个球员,一个与WSAD钥匙和一个带Arrow keys:

在这里插入图片描述

5、碰撞检测

好的,让我们添加一个输掉我们比赛的条件。一名球员一进墙就会输掉这场比赛。

我们已经添加了物理成分(对撞机和刚体)我们现在要做的就是增加一个新的OnTriggerEnter2D对我们移动剧本。如果玩家与某物发生碰撞,统一将自动调用此函数:

void OnTriggerEnter2D(Collider2D co) {
    // Do Stuff...
}

“Collider2D Co参数是玩家与之碰撞的对撞机。让我们确保这个对撞机不是玩家目前拖在身后的墙:

void OnTriggerEnter2D(Collider2D co) {
    // Not the current wall?
    if (co != wall) {
        // Do Stuff...
    }
}

在这种情况下,它必须是任何其他墙壁,这意味着玩家输了游戏。我们只会在这里保持简单毁灭玩家:

void OnTriggerEnter2D(Collider2D co) {
    // Not the current wall?
    if (co != wall) {
        print("Player lost:" + name);
        Destroy(gameObject);
    }
}

注意:在这一点上可以随意添加一些输赢的屏幕。

6、内容扩展

我们刚刚在Unity中创建了一个电子风格的光循环2D游戏。和往常一样,大多数功能都很容易实现-多亏了这个强大的游戏引擎。这款游戏提供了很多潜力,还有各种各样的功能可以添加:

  • 输赢屏幕
  • 2名以上玩家
  • 在线多人
  • AI
  • 一些特效
  • 更好的Sprites等等
    和往常一样,现在该由读者来让游戏变得有趣了。
没有更多推荐了,返回首页