寻数尘缘(1.0)

欢迎上github品鉴:ikuen14557/leon-s-project: cool (github.com)

注:本文只是对于项目的回顾,参考价值并不大。

再注:本项目创建于鄙人纯小白时期,项目可能会出现代码逻辑混乱,性能消耗巨大等问题,不建议用于大型游戏开发

人物相关

人物移动逻辑

一般写在FixedUpdate(每帧调用)

float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");//从inputsystem获取按键反馈
move = new Vector2(horizontal, vertical);
Vector2 postition = transform.position;
postition = postition + movespeed * move * Time.fixedDeltaTime;
rigidbody2d.MovePosition(postition);//移动

人物动画机

一般有方向的移动会使用状态树

if (!Mathf.Approximately(move.x, 0) || !Mathf.Approximately(move.y, 0))
{
    lookDirection.Set(move.x, move.y);
    lookDirection.Normalize();//将向量归一
    animator.SetFloat("MoveValue", 1);//将idle状态转换为walk
}//Mathf.Approximately 是 Unity 提供的用于比较浮点数是否近似的函数。它比直接使用 == 运算符更可靠
animator.SetFloat("MoveX", lookDirection.x);
animator.SetFloat("MoveY", lookDirection.y);

摄像机跟随

插件法

在maincamera加入插件

再新建一个gameobject加入插件

背包系统(基于ScriptObject)

官方文档见:ScriptableObject - Unity 手册 --- ScriptableObject - Unity 手册

GUI设计

这里我就不说怎么创建该UI了,只能说easy(值得一提的是有个Grid插件可以实现子对象整齐排列)

物品

创建名为“Item”的c#脚本代表物品属性

[CreateAssetMenu (fileName ="New Item",menuName ="Inventory/New Item")]//创建路径
public class Item : ScriptableObject 
{
    public string itemName;
    public Sprite itemImage;//图片
    public int itemHeld;//数量
    [TextArea]
    public string itemInfo;

    public bool equip;//是否可以装备
}

c#脚本名为“itemonworld”物体在场景中的管理 挂载在世界物品上

public class ItemOnWorld : MonoBehaviour
{
    public Item thisItem;//属性
    public Inventory playerInventory;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.CompareTag("player"))//判断玩家
        {
            AddNewItem();
            Destroy (gameObject);
        }
    }

    public void AddNewItem()
    {
        if(!playerInventory.itemList.Contains(thisItem))//如果在背包里面没有
        { 
            playerInventory.itemList.Add(thisItem);
            InventoryManager.CreateNewItem(thisItem);
        }
        else
        {
            thisItem.itemHeld += 1;
        }
    }
}

挂载在预制体上的脚本“slot”

public class Slot : MonoBehaviour
{
    public Item slotItem;
    public Image slotImage;
    //public Text slotNum;

    public void ItemOnClicked()
    {
        InventoryManager .UpdateItemInfo (slotItem .itemInfo);
    }
}

背包管理

背包列表

[CreateAssetMenu (fileName ="New Inventory",menuName ="Inventory/New Inventory")]
public class Inventory :ScriptableObject 
{ 
    public List<Item> itemList = new List<Item>();//list存放item
   
    public void AddItem(Item item)
    {
        itemList.Add(item);
    }

    public void RemoveItem(Item item)
    {
        itemList.Remove(item);
    }
}

背包管理

public class InventoryManager : MonoBehaviour
{
    static InventoryManager instance;

    public Inventory inventory;

    public GameObject slotGrid;//背包UI
    public Slot slotPrefab;
    public Text itemInfromation;

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

        foreach (var item in inventory.itemList)
        {
            CreateNewItem(item);
        }
    }

    private void OnEnable()
    {
        instance.itemInfromation.text = "";
    }

    public static void UpdateItemInfo(string itemDescription)
    {
        instance.itemInfromation.text = itemDescription;
    }


    public static void CreateNewItem(Item item)//生成物品并设置各种属性
    {
        
        Slot newItem = Instantiate(instance.slotPrefab,    instance.slotGrid.transform.position, Quaternion.identity);
        newItem.gameObject.transform.SetParent(instance.slotGrid.transform);
        newItem.slotItem = item;
        newItem.slotImage.sprite = item.itemImage;
        newItem.gameObject.transform.localScale = new Vector3 (1,1,1);
    }
}

对话系统

打字机效果(基于协程处理)

unity官方文档:MonoBehaviour-StartCoroutine - Unity 脚本 API

public class DialogSystem : MonoBehaviour
{
    [Header("UI组件")]
    public Text textLabel1;
    public Text textLabel2;

    [Header("文本文件")]
    public TextAsset textFile;
    public TextAsset textname;
    public int index;
    public float textspeed;

    bool textFinished;//是否完成打字
    bool cancelTyping;//取消打字
    List<string> textList = new List<string>();
    List<string> textnamelist = new List<string>();

    void Awake()
    {
        GetTextFormFile(textFile, textname);//将Text按照‘\n’切开
    }

    private void OnEnable()
    {
        textFinished = true;
        StartCoroutine(SetTextUI());//启用协程
    }
   
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Q) && index == textList.Count)//当对话完后关闭对话框
        {
            gameObject.SetActive(false);
            index = 0;
            return;
        }
        if (Input.GetKeyDown(KeyCode.Q))//跳过打字
        {
            if (textFinished && !cancelTyping)
            {
                StartCoroutine(SetTextUI());
            }
            else if (!textFinished)
            {
                cancelTyping = !cancelTyping;
            }
        }
    }

    void GetTextFormFile(TextAsset file, TextAsset file1)
    {
        textList.Clear();
        textnamelist.Clear();
        index = 0;
        var lineDate1 = file1.text.Split('\n');
        foreach (var line in lineDate1)
        {
            textnamelist.Add(line);
        }
        var lineDate = file.text.Split('\n');
        foreach (var line in lineDate)
        {

            textList.Add(line);
        }
    }

    IEnumerator SetTextUI()//实现一个一个字输出
    {
        textFinished = false;
        textLabel2.text = "";
        textLabel1.text = "";
        for (int i = 0; i < textnamelist[index].Length && !cancelTyping; i++)
        {
            textLabel1.text += textnamelist[index][i];
            yield return new WaitForSeconds(textspeed);
        }
        for (int i = 0; i < textList[index].Length && !cancelTyping; i++)
        {
            textLabel2.text += textList[index][i];
            yield return new WaitForSeconds(textspeed);
        }
        textLabel1.text = textnamelist[index];
        textLabel2.text = textList[index];
        cancelTyping = false;
        textFinished = true;
        index++;
    }
}

加速系统(对象池)

创建对象池

public class ShadowPool : MonoBehaviour
{
    public static ShadowPool instance;
    public GameObject shadowPrefab;

    public int shadowCount;

    Queue<GameObject> availableObject = new Queue<GameObject>();//队列


    private void Awake()
    {
        instance = this;

        FillPool();//初始化对象池
    }

    public void FillPool()//填满对象池
    {
        for(int i=0;i<10;i++)
        {
            var newShandow = Instantiate(shadowPrefab);
            newShandow.transform.SetParent(transform);

            //取消启用
            ReturnPool(newShandow);
        }
    }


    public void ReturnPool(GameObject pool)//取消启用
    {
        pool.SetActive(false);

        availableObject.Enqueue(pool);//将对象加入队列中
    }

    public GameObject GetFormPool()
    {
        if(availableObject.Count == 0)
        {
            FillPool();
        }
        var outShadow = availableObject.Dequeue();//移除开头元素并返回

        outShadow.SetActive(true);
        return outShadow;
    }
}

控制残影的基本属性 

public class Shandow : MonoBehaviour
{
    Transform player;

    SpriteRenderer thisSprite;
    SpriteRenderer playerSprite;

    Color color;

    [Header("时间控制参数")]
    public float activeTime;//显示时间
    public float activeStart;//开始显示时间

    [Header("不透明度控制")]
    float alpha;
    public float alphaSet;//初始值
    public float alphaMultiplier;


    private void OnEnable()
    {
        player = GameObject.Find("peter").transform;
        thisSprite = GetComponent<SpriteRenderer>();
        playerSprite =player.GetComponent<SpriteRenderer>();

        alpha = alphaSet;

        thisSprite.sprite = playerSprite.sprite;

        transform.position = player.transform.position;
        transform.rotation = player.transform.rotation;
        transform.localScale = player.transform.localScale;


        activeStart = Time.time;
    }


    void Update()
    {
        alpha *= alphaMultiplier;

        color = new Color(1, 1, 1, alpha);

        thisSprite.color = color;

        if(Time.time >= activeTime+activeStart)
        {
            //返回对象池
            ShadowPool.instance.ReturnPool(this.gameObject);
        }
    }
}

人物冲刺控制

 void ReadyToDash()//当冷却时间结束
 {
     isDash = true;

     dashTimeLeft = dashTime;

     lastdash = Time.time;
 }

 void Dash()
 {
     if(isDash)
     {
         if(dashTimeLeft > 0)
         {
             rigidbody2d .velocity = new Vector2(dashSpeed * horizontal, rigidbody2d.velocity.y);

             dashTimeLeft -= Time.deltaTime;

             ShadowPool.instance.GetFormPool();
         }

         else
         {
             isDash = false;
         }
     }
 }

关卡设计

华容道

root如下

public class GameManger2 : MonoBehaviour
{
    public static GameManger2 Inst;
    public GameObject CeilItem;
    public List<Sprite> mapSps;//图片素材
    public int row = 3;//行
    public int col = 3;//列
   
    //洗牌
    
    void WashNumbers(List <int>listObj)
    {
        //模拟法
        int q = Random.Range(1, 51);//交换次数
        int zerolocation = 8;//空格的位置
        while(q>0)
        {
            int j = Random.Range(0, listObj.Count);
            if(j==zerolocation -3||j==zerolocation -1||j==zerolocation +1||j==zerolocation +3)
            {
                q--;
                int temp = listObj[zerolocation ];
                listObj[zerolocation ]= listObj[j];
                listObj[j] = temp;
                zerolocation = j;
            }
        }
        while (zerolocation !=8)//让空格回归左下角
        {
            int j = Random.Range(0, listObj.Count);
            if (j == zerolocation - 3 || j == zerolocation - 1 || j == zerolocation + 1 || j == zerolocation + 3)
            {
                int temp = listObj[zerolocation];
                listObj[zerolocation] = listObj[j];
                listObj[j] = temp;
                zerolocation = j;
            }
        }
    }

    public List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 0 };//数字顺序

    //key是numbe
    private Dictionary<int, CeilItem> dataDict = new Dictionary<int, CeilItem>();//将实例化出的网格存储入字典

    public void clear()//胜利后清除所有网格
    {
        foreach (var pair in dataDict)
        {
            // 获取 GameObject 的引用
            GameObject go = pair.Value.gameObject;

            // 销毁 GameObject
            Destroy(go);
        }
    }

    CeilItem findCeilItem(int rIdx, int cIdx)//寻找指定格子
    {
        foreach (KeyValuePair<int, CeilItem> kv in dataDict)
        {
            if (kv.Value.rInx == rIdx && kv.Value.cIdx == cIdx)
            {
                return kv.Value;
            }
        }
        return null;
    }


    Vector3 getCeilItemPos(int i, int j)//给实例化的网格分配
    {
        Vector3 ve = default;
        ve.x = j;
        ve.y = -1 * i;
        return ve;
    }
   
    bool checkCeilRight(int rIdex, int cIdex) //检查格子是否合规
    {
        if (rIdex < 0 || rIdex >= row || cIdex < 0 || cIdex >= col)
        {
            return false;
        }
        return true;
    }
    
    bool findOneZeroCeil(int rIdx, int cIdx)//执行一次是否能查找空格子
    {
        if (!checkCeilRight(rIdx, cIdx))
        {
            return false;
        }
        CeilItem ceil = findCeilItem(rIdx, cIdx);

        if (ceil != null)
        {
            return ceil.number == 0;
        }
        return false;
    }

    public (int, int) findfourZeroCel(int rIdx, int cIdx)
    {
        List<(int, int)> dirs = new List<(int, int)>()//可以交换的格子字典
        {
            (rIdx + 1, cIdx),
            (rIdx - 1, cIdx),
            (rIdx, cIdx + 1),
            (rIdx, cIdx - 1)
        };
        for (int i = 0; i < dirs.Count; i++)
        {
            (int, int) item = dirs[i];
            if (findOneZeroCeil(item.Item1, item.Item2))//如果周围有空格就返回空格位置
            {
                return item;
            }
        }
        return (999, 999);//没找到
    }

    public void CheckSwap(int rIdx, int cIdx, int clickCeilNumber)//检查是否可以交换
    {
        (int, int) res = findfourZeroCel(rIdx, cIdx);
        if (res.Item1 != 999)
        {
            StartSwap(clickCeilNumber, 0);//开始交换
        }
    }

    void StartSwap(int clickNumber, int zeroNumber)//开始交换
    {
        CeilItem clickItem = dataDict[clickNumber];
        CeilItem zeroItem = dataDict[0];

        Vector3 clickPos = clickItem.transform.position;
        clickItem.transform.position = zeroItem.transform.position;
        zeroItem.transform.position = clickPos;
        SwapData(clickItem, zeroItem);
    }

    void SwapData(CeilItem clickItem, CeilItem zeroItem)//交换行列信息
    {
        var clickRIdx = clickItem.rInx;
        var clickCIdx = clickItem.cIdx;
        clickItem.updateRandCIdx(zeroItem.rInx, zeroItem.cIdx);
        zeroItem.updateRandCIdx(clickRIdx, clickCIdx);
    }

    public bool isWin()
    {
        bool res = false;
        int startNum = 1;
        int maxNum = row * col;
        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < col; j++)
            {
                var ceil = findCeilItem(i, j);//根据行列判断是否在正确位置
                if (startNum ==maxNum )
                {
                    res = ceil.number ==0;
                    continue;
                }
                if(ceil .number != startNum)
                {
                    return res;
                }
                startNum ++;
            }
        }
        if(res)
        {
            Debug.Log("游戏胜利");
        }
        return res;
    }
        void InitMap()
        {
            WashNumbers(numbers);
            int numberIdx = 0;
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < col; j++)
                {
                    Vector3 pos = getCeilItemPos(i, j);// new Vector3(i, j, 0);
                    var go = GameObject.Instantiate(CeilItem, pos, Quaternion.identity);//实例化
                    var ceilItem = go.GetComponent<CeilItem>();
                    int number = numbers[numberIdx];
                    ceilItem.Init(number, i, j);
                    dataDict.Add(number, ceilItem);
                    numberIdx++;
                }
            }
        }
       
        void Awake()
        {
            Inst = this; ;
        }
        void Start()
        {
            InitMap();
        }
}

预制体ceiltem

public class CeilItem : MonoBehaviour
{
    public  int rInx { get; private set; }
    public  int cIdx { get; private set; }
    public  int number;
    private SpriteRenderer sp;
    public static CeilItem instance;

    private void Awake()
    {
        instance = this;
        sp = GetComponent<SpriteRenderer>();
    }
    public void Init(int number, int rInx,int cIndx)//将信息带入物品
    {
        this.number = number;
        this.rInx = rInx;
        this.cIdx = cIndx;
        UpdateSP(this.number);
    }

    public void updateRandCIdx(int rIdx,int cIdx)
    {
        this.rInx=rIdx;
        this.cIdx = cIdx;
    }
    private void OnMouseDown()//点击鼠标后开始检查
    {
        if (number == 0)
            return;
        GameManger2.Inst.CheckSwap(rInx, cIdx,number);
    }

    public  void UpdateSP(int number)
    {
       if(number==0)
        {
            sp.sprite = null;
        }
        else
        {
            int idx = number - 1;
            sp.sprite = GameManger2.Inst.mapSps[idx];
        }
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值