Unity笔记-15-2048小游戏

Unity笔记-15-2048小游戏

搭建UI框架

首先创建一个Canvas画布,作为UI的承载体

创建一个Panel取名为PanelMain,作为游戏的主要运行画面

PanelMain中创建一个Image作为游戏主要画面的背景,给予Grid Layout Group组件,设置对应的参数,以达到让它的Child-Image对象能按行列准确排列即可,如下图

在这里插入图片描述

然后依次制作出完成UI,如下图

在这里插入图片描述

GameOver会有事件去控制,这里先做好UI即可

脚本部分

主要分三大类:1.游戏控制类;2.精灵(图片)功能类;3.游戏核心;4.资源读取类;

游戏控制类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using Core2048;
//设置枚举类型为Core2048里的枚举,防止枚举类型冲突
using MoveDirection = Core2048.MoveDirection;

public class GameControllerDemo : MonoBehaviour,IPointerDownHandler,IDragHandler
{
    /// <summary>
    /// 游戏核心算法
    /// </summary>
    private GameCoreDemo core;
    /// <summary>
    /// 每个位置的精灵脚本引用
    /// </summary>
    private NumberSetDemo[,] spriteAction;
    /// <summary>
    /// 开始
    /// </summary>
    private GameObject gameOver;
    private void Start()
    {
        gameOver = transform.parent.transform.Find("GameOver").gameObject;
        gameOver.SetActive(false);
        spriteAction = new NumberSetDemo[4, 4];
        core = new GameCoreDemo();
        Init();
        CreateNumber();
        CreateNumber();
    }
    /// <summary>
    /// 初始化地图
    /// </summary>
    private void Init()
    {
        for(int r = 0; r < 4; r++)
        {
            for(int c = 0; c < 4; c++)
            {
                CreateSprite(r,c);
            }
        }
    }
    /// <summary>
    /// 创建精灵
    /// </summary>
    /// <param name="r"></param>
    /// <param name="c"></param>
    private void CreateSprite(int r,int c)
    {
        GameObject go = new GameObject(r.ToString()+c.ToString());
        go.AddComponent<Image>();
        NumberSetDemo set= go.AddComponent<NumberSetDemo>();
        spriteAction[r, c] = set;//储存该位置的精灵脚本引用
        set.SetNumber(0);
        go.transform.SetParent(this.transform,false);
    }
    /// <summary>
    /// 创建新数字
    /// </summary>
    private void CreateNumber()
    {
        LocationDemo loc;
        int number;
        core.GenerateNumber(out loc, out number);
        spriteAction[loc.RIndex, loc.CIndex].SetNumber(number);
        spriteAction[loc.RIndex, loc.CIndex].CreateEffect();
    }
    private void Update()
    {
        if (core.IsChange)
        {
            UpdateMap();
            CreateNumber();
            core.IsChange = false;
            AllMergeEffect();
            AllMoveEffect();
            ResetAll();
            UpdateScore();
            Invoke("IsGameOver", 0.3f);
        }
    }
    private void ResetAll()
    {
        //重置前位置地图
        core.ReProData(1);
        //core.ReProData(2);
        //重置合并集合
        core.mergeList.Clear();
    }
    /// <summary>
    /// 所有精灵的移动动画
    /// </summary>
    private void AllMoveEffect()
    {
        for (int r = 0; r < 4; r++)
        {
            for (int c = 0; c < 4; c++)
            {
                if (core.preMap[r, c] != null)
                {
                    spriteAction[r, c].MoveEffect(spriteAction[core.preMap[r, c].RIndex, core.preMap[r, c].CIndex].transform.position);
                }
            }
        }
    }
    /// <summary>
    /// 精灵的合并动画
    /// </summary>
    private void AllMergeEffect()
    {
        foreach (var item in core.mergeList)
        {

            LocationDemo loc = item as LocationDemo;
            spriteAction[loc.RIndex, loc.CIndex].MergeEffect();
        }
    }
    /// <summary>
    /// 更新精灵
    /// </summary>
    private void UpdateMap()
    {
        for(int r = 0; r < 4; r++)
        {
            for(int c = 0; c < 4; c++)
            {
                spriteAction[r, c].SetNumber(core.Map[r,c]);
            }
        }
    }
    /// <summary>
    /// 更新分数
    /// </summary>
    private void UpdateScore()
    {
        transform.parent.transform.Find("ScoreText").GetComponent<Text>().text=core.score.ToString();
    }
    /// <summary>
    /// 鼠标开始位置
    /// </summary>
    private Vector2 startPos;
    /// <summary>
    /// 鼠标是否按下
    /// </summary>
    private bool isDown=false;
    /// <summary>
    /// 鼠标按下事件
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerDown(PointerEventData eventData)
    {
        startPos = eventData.position;
        isDown = true;
    }
    /// <summary>
    /// 鼠标拖动事件
    /// </summary>
    /// <param name="eventData"></param>
    public void OnDrag(PointerEventData eventData)
    {
        if (isDown==false)
        {
            return;
        }
        Vector2 offset = eventData.position-startPos;
        float x = Mathf.Abs(offset.x);
        float y = Mathf.Abs(offset.y);
        MoveDirection dir;
        if (x >= y)//水平
        {
            dir = offset.x > 0 ? MoveDirection.Right : MoveDirection.Left;
        }
        else//垂直
        {
            dir = offset.y > 0 ? MoveDirection.Up : MoveDirection.Down;
        }
        core.Move(dir);
        isDown = false;
    }
    /// <summary>
    /// 重新开始
    /// </summary>
    public void ReStart()
    {
        for (int r = 0; r < 4; r++)
        {
            for (int c = 0; c < 4; c++)
            {
                spriteAction[r, c].SetNumber(0);
            }
        }
        gameOver.SetActive(false);
        core = new GameCoreDemo();
        UpdateScore();
        CreateNumber();
        CreateNumber();
    }
    /// <summary>
    /// 游戏结束
    /// </summary>
    private void IsGameOver()
    {
        if (core.IsOver())
        {
            gameOver.SetActive(true);
        }
    }
    /// <summary>
    /// 退出游戏
    /// </summary>
    public void Exit()
    {
//        UnityEditor.EditorApplication.isPlaying = false;
        Application.Quit();
    }
}

精灵功能类

public class NumberSetDemo : MonoBehaviour
{
    /// <summary>
    /// 图片组件引用
    /// </summary>
    private Image img;
    private void Awake()
    {
        img = GetComponent<Image>();   
    }
    /// <summary>
    /// 设置精灵数字
    /// </summary>
    /// <param name="number"></param>
    public void SetNumber(int number)
    {
        img.sprite = ResourcesLoadDemo.SpriteLoad(number);
    }
    /// <summary>
    /// 精灵创建动画
    /// </summary>
    public void CreateEffect()//创建动画
    {
        iTween.ScaleFrom(this.gameObject,Vector3.zero,0.3f);
    }
    /// <summary>
    /// 精灵合并动画
    /// </summary>
    public void MergeEffect()//合并动画
    {
        iTween.ScaleFrom(this.gameObject,Vector3.one*1.2F,0.3f);
    }
    /// <summary>
    /// 精灵移动动画
    /// </summary>
    /// <param name="target"></param>
    public void MoveEffect(Vector3 target)
    {
        iTween.MoveFrom(this.gameObject,iTween.Hash(
            "position",target,
            "speed",100,
            "easetype",iTween.EaseType.easeOutElastic
            ));
    }
}

游戏核心类

提供,游戏核心算法功能的类,相对独立

namespace Core2048
{
    /// <summary>
    /// 2048核心算法类
    /// </summary>
    public class GameCoreDemo
    {
        /// <summary>
        /// 得分
        /// </summary>
        public int score;
        /// <summary>
        /// 当前地图布局
        /// </summary>
        private int[,] map;
        /// <summary>
        /// 本次变换前的地图布局,以下简称前地图
        /// </summary>
        private int[,] originalMap;
        /// <summary>
        /// 移动行/列的单独提取数组,以下简称单组
        /// </summary>
        private int[] mergeArray;
        /// <summary>
        /// 后移所有零的提取数组,以下简称去零单组
        /// </summary>
        private int[] removeZeroArray;
        /// <summary>
        /// 随机数
        /// </summary>
        private System.Random random;
        /// <summary>
        /// 空位置布局
        /// </summary>
        private List<LocationDemo> emptyLOC;

        /// <summary>
        /// 合并位置的集合
        /// </summary>
        public List<LocationDemo> mergeList;
        /// <summary>
        /// 移动行/列的单独提取合并位置的数组,以下简称合并单组
        /// </summary>
        public int[] mergePoint;//初始值为负表示空
        /// <summary>
        /// 上述数组的索引
        /// </summary>
        public int mergeIndex;

        /// <summary>
        /// 当前地图每个位置移动前的位置,以下简称前位置地图
        /// </summary>
        public LocationDemo[,] preMap;
        /// <summary>
        /// 移动行/列的单独提取移动前的位置数组,以下简称位置单组
        /// </summary>
        public int[] prePoint;
        /// <summary>
        /// 上述数组的索引
        /// </summary>
        public int preIndex = 0;
        /// <summary>
        /// 移动行/列的单独提取数组的非零数字
        /// </summary>
        public int preLength;

        /// <summary>
        /// 当前地图属性
        /// </summary>
        public int[,] Map
        {
            get
            { return map; }
        }
        /// <summary>
        /// 核心算法构造函数-初始化
        /// </summary>
        public GameCoreDemo()
        {
            //初始化得分
            score = 0;
            //初始化前位置地图
            preMap = new LocationDemo[4, 4];
            //初始化位置单组
            prePoint = new int[4];
            //初始化合并单组
            mergePoint = new int[] { -1, -1 };//注意:由于行列固定为4,因此单组合并的次数最大为2
            //mergeIndex = 0;
            //初始化地图
            map = new int[4, 4];
            //初始化单组
            mergeArray = new int[map.GetLength(0)];
            //初始化去零单组
            removeZeroArray = new int[4];
            //初始化空位置布局
            emptyLOC = new List<LocationDemo>(16);
            //初始化随机数
            random = new System.Random();
            //初始化前地图
            originalMap = new int[4, 4];
            //初始化合并位置集合
            mergeList = new List<LocationDemo>(8);
        }
        /// <summary>
        /// 合并单组重置
        /// </summary>
        public void MergePointReStart()
        {
            mergePoint[0] = -1;
            mergePoint[1] = -1;
            mergeIndex = 0;
        }
        /// <summary>
        /// 位置单组以及前位置地图重置
        /// </summary>
        /// <param name="number"></param>
        public void ReProData(int number)
        {
            switch (number)
            {
                case 1://重置前位置地图
                    preMap = new LocationDemo[4, 4];
                    break;
                case 2://重置位置单组
                    preIndex = 0;
                    preLength = 0;
                    Array.Clear(prePoint, 0, prePoint.Length);
                    break;
            }
           
        }
        /// <summary>
        /// 后移单组的所有零,简称去零
        /// </summary>
        private void RemoveZero()
        {
            Array.Clear(removeZeroArray, 0, 4);
            int index = 0;
            for (int i = 0; i < mergeArray.Length; i++)
            {
                if (mergeArray[i] != 0)
                {
                    removeZeroArray[index++] = mergeArray[i];//1
                    if (i == mergePoint[mergeIndex])//如果当前的i与合并位置相同,则赋给新去零后的合并位置
                    {
                        mergePoint[mergeIndex++] = index - 1;
                    }
                } 
            }
            mergeIndex = 0;
            removeZeroArray.CopyTo(mergeArray, 0);
        }
        /// <summary>
        /// 合并
        /// </summary>
        private void Merge()
        {
            //单组合并前,首先调用去零
            RemoveZero();
            //判断前后位置是否相同,有则合并
            for (int i = 0; i < mergeArray.Length - 1; i++)
            {
                if (mergeArray[i] != 0 && mergeArray[i] == mergeArray[i + 1]&&mergeArray[i]!=2048)
                {
                    mergeArray[i] += mergeArray[i + 1];
                    score += mergeArray[i];
                    mergeArray[i + 1] = 0;
                    //记录合并位置,合并单组索引自增
                    mergePoint[mergeIndex++] = i;
                }
            }
            //合并完成,索引归零,因为下一行/列会更新单组
            mergeIndex = 0;
            //调用去零
            RemoveZero();
        }
        /// <summary>
        /// 记录单组变化前的位置
        /// </summary>
        private void RecordPrePoint()
        {
            for(int i = 0; i < mergeArray.Length; i++)
            {
                if (mergeArray[i] != 0)
                {
                    prePoint[preIndex] = i;//按照数字顺序拿到之前的位置
                    preIndex++;
                }
            }
            //记录有效数字
            preLength = preIndex;
            //位置单组索引归零
            preIndex = 0;
        }
        /// <summary>
        /// 向上移动
        /// </summary>
        private void MoveUp()
        {
            for (int c = 0; c < map.GetLength(1); c++)
            {
                //首先提取每行/列到单组
                for (int r = 0; r < map.GetLength(0); r++)
                    mergeArray[r] = map[r, c];
                //记录原位置
                RecordPrePoint();
                //合并
                Merge();
                //单组回归地图
                for (int r = 0; r < map.GetLength(0); r++)
                {
                    map[r, c] = mergeArray[r];
                    //把原位置记录到集合
                    if (preIndex<preLength&&mergeArray[r]!=0)
                    {
                        preMap[r, c] = new LocationDemo(prePoint[preIndex], c);
                        preIndex++;
                    }
                    //把合并位置记录到集合
                    if (mergeIndex < 2)
                    {
                        if (r == mergePoint[mergeIndex])
                        {
                            mergeList.Add(new LocationDemo(r, c));
                        }
                    }

                }
                //重置位置单组
                ReProData(2);
                //重置合并单组
                MergePointReStart();
            }
        }
        /// <summary>
        /// 向下移动
        /// </summary>
        private void MoveDown()
        {
            for (int c = 0; c < map.GetLength(1); c++)
            {
                for (int r = map.GetLength(0) - 1; r >= 0; r--)
                {
                    mergeArray[3 - r] = map[r, c];
                }
                RecordPrePoint();
                Merge();

                for (int r = map.GetLength(0) - 1; r >= 0; r--)
                {
                    map[r, c] = mergeArray[3 - r];
                    if (preIndex < preLength && mergeArray[3-r] != 0)
                    {
                        preMap[r, c] = new LocationDemo(3-prePoint[preIndex], c);
                        preIndex++;
                    }
                    if (mergeIndex < 2)
                    {
                        if ((3 - r) == mergePoint[mergeIndex])
                        {
                            mergeList.Add(new LocationDemo(r, c));
                            mergeIndex++;
                        }
                    }
                }
                ReProData(2);
                MergePointReStart();
            }
        }
        /// <summary>
        /// 向左移动
        /// </summary>
        private void MoveLeft()
        {
            for (int r = 0; r < 4; r++)
            {
                for (int c = 0; c < 4; c++)
                    mergeArray[c] = map[r, c];
                RecordPrePoint();
                Merge();

                for (int c = 0; c < 4; c++)
                {
                    map[r, c] = mergeArray[c];
                    if (preIndex < preLength && mergeArray[c] != 0)
                    {
                        preMap[r, c] = new LocationDemo(r,prePoint[preIndex]);
                        preIndex++;
                    }
                    if (mergeIndex < 2)
                    {
                        if (c == mergePoint[mergeIndex])
                        {
                            mergeList.Add(new LocationDemo(r, c));
                            mergeIndex++;
                        }
                    }
                }
                ReProData(2);
                MergePointReStart();
            }
        }
        /// <summary>
        /// 向右移动
        /// </summary>
        private void MoveRight()
        {
            for (int r = 0; r < 4; r++)
            {
                for (int c = 3; c >= 0; c--)
                    mergeArray[3 - c] = map[r, c];
                RecordPrePoint();
                Merge();

                for (int c = 3; c >= 0; c--)
                {
                    map[r, c] = mergeArray[3 - c];
                    if (preIndex < preLength && mergeArray[3-c] != 0)
                    {
                        preMap[r, c] = new LocationDemo(r,3-prePoint[preIndex]);
                        preIndex++;
                    }
                    if (mergeIndex < 2)
                    {
                        if ((3 - c) == mergePoint[mergeIndex])
                        {
                            mergeList.Add(new LocationDemo(r, c));
                            mergeIndex++;
                        }
                    }
                }
                ReProData(2);
                MergePointReStart();
            }
        }

        /// <summary>
        /// 地图改变与否
        /// </summary>
        public bool IsChange { get; set; }
        /// <summary>
        /// 移动主调用方法
        /// </summary>
        /// <param name="direction"></param>
        public void Move(MoveDirection direction)
        {
            //移动前记录Map
            Array.Copy(map, originalMap, map.Length);
            IsChange = false;//假设没有发生改变
            switch (direction)
            {
                case MoveDirection.Up: MoveUp(); break;
                case MoveDirection.Down: MoveDown(); break;
                case MoveDirection.Left: MoveLeft(); break;
                case MoveDirection.Right: MoveRight(); break;
            }
            //移动后对比  重构 --> 提取方法
            //MergePointReStart();
            //检查地图是否改变
            CheckMapChange();
        }
        /// <summary>
        /// 检查地图是否改变
        /// </summary>
        private void CheckMapChange()
        {
            for (int r = 0; r < map.GetLength(0); r++)
            {
                for (int c = 0; c < map.GetLength(1); c++)
                {
                    if (map[r, c] != originalMap[r, c])
                    {
                        IsChange = true;//发生改变
                        return;
                    }
                }
            }
        }
        /// <summary>
        /// 计算空位置,并将空位置添加到空位置集合
        /// </summary>
        private void CalculateEmpty()
        {

            emptyLOC.Clear();
            for (int r = 0; r < map.GetLength(0); r++)
            {
                for (int c = 0; c < map.GetLength(1); c++)
                {
                    if (map[r, c] == 0)
                    {
                        emptyLOC.Add(new LocationDemo(r, c));
                    }
                }
            }
        }
        /// <summary>
        /// 创建新数字
        /// </summary>
        /// <param name="loc"></param>
        /// <param name="newNumber"></param>
        public void GenerateNumber(out LocationDemo loc, out int newNumber)
        {
            CalculateEmpty();
            if (emptyLOC.Count > 0)
            {
                int emptyLocIndex = random.Next(0, emptyLOC.Count);//0,15

                loc = emptyLOC[emptyLocIndex];//有空位置的list  3

                newNumber = map[loc.RIndex, loc.CIndex] = (random.Next(0, 10) == 1) ? 4 : 2;

                //将该位置清除
                emptyLOC.RemoveAt(emptyLocIndex);
            }
            else
            {
                //注意:int? a加了问好之后就会从值类型变成引用类型,被成为可空值类型,不再是原来的值类型,如果要获取值类型数据,要用a.Value但这是只读的
                newNumber = -1;
                loc = new LocationDemo(-1, -1);
            }
        }
        /// <summary>
        /// 游戏是否结束
        /// </summary>
        public bool IsOver()
        {
            if (emptyLOC.Count > 0) return false;
            #region 省略
            //水平
            //for (int r = 0; r < 4; r++)
            //{
            //    for (int c = 0; c < 3; c++)
            //    {
            //        if (map[r, c] == map[r, c + 1])
            //            return false;
            //    } 
            //}
            垂直
            //for (int c = 0; c < 4; c++)
            //{
            //    for (int r = 0; r < 3; r++)
            //    {
            //        if (map[r, c] == map[r + 1, c])
            //            return false;
            //    } 
            //}
            #endregion
            for (int r = 0; r < 4; r++)
            {
                for (int c = 0; c < 3; c++)
                {
                    //检查是否有合并空间
                    if (map[r, c] == map[r, c + 1] || map[c, r] == map[c + 1, r])
                        return false;
                }
            }

            return true;//游戏结束 
        }
    }
}
namespace Core2048
{
    public enum MoveDirection
    {
        //******定义值*******
        Up,
        Down,
        Left,
        Right
    }
}
namespace Core2048
{
    public class LocationDemo
    {
        /// <summary>
        /// 行索引
        /// </summary>
        public int RIndex { get; set; }
        /// <summary>
        /// 列索引
        /// </summary>
        public int CIndex { get; set; }
        public LocationDemo(int r, int c)
        {
            this.RIndex = r;
            this.CIndex = c;
        }
    }
}

资源读取类

public class ResourcesLoadDemo
{
    /// <summary>
    /// 资源字典
    /// </summary>
    private static Dictionary<int, Sprite> spriteNumber;
    /// <summary>
    /// 静态构造初始化资源
    /// </summary>
    static ResourcesLoadDemo()
    {
        spriteNumber = new Dictionary<int, Sprite>();
        var allSprite = Resources.LoadAll<Sprite>("2048Demo/2048");
        foreach (var item in allSprite)
        {
            spriteNumber.Add(int.Parse(item.name),item);
        }
    }
    /// <summary>
    /// 读取资源
    /// </summary>
    /// <param name="number"></param>
    /// <returns></returns>
    public static Sprite SpriteLoad(int number)
    {
        return spriteNumber[number];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值