找回密码
 立即注册
查看: 331|回复: 10

[笔记] 用U3D开发消灭星星(Pop Star)

[复制链接]
发表于 2023-2-12 09:32 | 显示全部楼层 |阅读模式
某大学计算机专业的学生,接触u3d四个月了,正巧赶上学校Java有一个开发PopStar的实验课,寻思着用unity做一做。同时把这个小工程当做自己这几个月学习的一个小测试,真正的独立去开发一个小游戏,找到自己的弱点和不足之处。此外,写这个博客记录一下自己unity3d的学习。各位u3d大佬手下留情,如果能指点一二那就更好了。

一,场景的搭建
1.准备工作:
          1)工程创建为2D,接着各种文件夹的创建;
          2)两个场景——游戏场景,菜单界面(涉及到皮肤的更换);
          3)素材资源的导入;
          4)素材的处理,主要是分割图片。
          5)导入Dotween动画插件,方便实现星星的缓动效果。(这里应该是有点大材小用了,主要是想偷个懒   :)   )


2.搭建菜单场景
          1)面子工程:历史最高分,最大关卡的显示(逻辑后面再实现)。


          2)主要功能:游戏的开始与退出的,皮肤切换,分数和关卡的保存。
          3)创建空物体GameManager挂载脚本GameManager01实现:a.menu场景->game场景的切换和游戏的退出;
                                                                                                            b.添加场景的序列号;
                                                                                                            c.记录玩家选择的皮肤;
                                                                                                            d.为Button添加鼠标点击事件;代码如下:
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameManager01 : MonoBehaviour
{

    public Toggle toggle1;//取得皮肤1的Toggle组件

    /**
     * 游戏退出
     */
    public void OnClickExit()
    {
        #if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
        #else
        Application.Quit();
        #endif
    }

    /**
     * 开始游戏
     */
    public void OnClickStart()
    {
        //开始游戏之前保存用户选择的皮肤
        if (toggle1.isOn)
        {
            PlayerPrefs.SetInt("SkinMode",0 );//皮肤设置为1
        }
        else
        {
            PlayerPrefs.SetInt("SkinMode", 1);//皮肤设置为2
        }

        print(PlayerPrefs.GetInt("SkinMode"));//输出,检测功能是否实现

        SceneManager.LoadScene(1);
    }
}

3.搭建游戏场景
          1)创建空物体StarGrid用于存放所有的星星,把其当做所有星星的父物体,统一进行管理
          2)新建Image,source image选中为红色星星,添加Star脚本,并将其他四个星星都作成prefab,并为不同的星星添加tag,代码如下:
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;

public class Star : MonoBehaviour
{

    public int row = 0;//行值
    public int col = 0;//列值

    private void Start()
    {
        SetPos();
    }

    /**
     * 根据行列值对星星的位置进行操作
     */
    public void SetPos()
    {
        transform.DOLocalMove(new Vector3(-258 + col*52 + 22.5f , 258 -row*52 - 22.5f , 0),0.5f);
    }

    /**
     * 通过行列值判断行星是否存在List中
     */
    public bool IsExist(List<Star> starList,int row,int col)
    {
        for (int i = 0; i < starList.Count; i++)
        {
            Star star = starList;
            if (star.row == row && star.col == col)
            {
                return true;
            }
        }

        return false;
    }

    /**
     * 清除单个星星的方法
     */
    public void Clear()
    {
        //TODO 星星清除的粒子特效
        this.transform.DOScale(new Vector3(0.01f, 0.01f, 0.01f), 0.3f);//借用了Dotween动画插件
        Destroy(this);//销毁
    }

}
          3)添加空物体GameManager挂载GameManager02脚本用于实现:
               a.星星矩阵的实例化;
private List<Star> CreateStarList()
    {
        List <Star> StarList = null;

        for (int i = 0; i < MAX_ROW; i++)
        {
            for (int j = 0; j < MAX_COL; j++)
            {
                GameObject gameobject = Instantiate(GetRandomPrefab(), Vector3.zero, Quaternion.identity);//实例化随机颜色的星星
                gameobject.transform.SetParent(starGrid);//把星星的父物体设置为starGrid
                gameobject.transform.localScale = new Vector3(1,1,1);//把局部的scale保持为原来一致,防止相机渲染导致大小的变化
                Star star = gameobject.GetComponent<Star>();//取得当前星星的引用
                star.row = i;//设置行值
                star.col = j;//设置列值
                star.SetPos();//位置的变化
                StarList.Add(star);//把星星添加到集合中
            }
        }

        return StarList;
    }

    /**
     * 随机获得五个星星prefab中的一个
     */
    private GameObject GetRandomPrefab()
    {
        int r = Random.Range(0, 5);
        GameObject prefab = redStarPrefab;
        switch (r)
        {
            case 0:
                prefab = redStarPrefab;
                break;
            case 1:
                prefab = blueStarPrefab;
                break;
            case 2:
                prefab = yellowStarPrefab;
                break;
            case 3:
                prefab = purpleStarPrefab;
                break;
            case 4:
                prefab = greenStarPrefab;
                break;
        }

        return prefab;
    }
               b.星星的消除,创建空物体StarManager挂载StarManager脚本用于实现星星的消除和位置的移动;
               实现获得待星星消除的方法,该方法在Star的鼠标点击事件里面进行调用。
               涉及了递归调用,递归死循环已经让我绝望了无数次了。
               具体的算法也不进行介绍了,还算比较简单
public static void FindStar(List<Star> currentStarList, Star star, List<Star> clearedStars)
    {
        int row = star.row;
        int col = star.col;

        Star tStar = null;

        if (col - 1 >= 0)
        {
            tStar = Star.LookupStar(currentStarList, row, col - 1);
            if (tStar != null && !Star.IsExisted(clearedStars, tStar))
            {
                if (tStar.tag == star.tag)
                {
                    clearedStars.Add(tStar);
                    FindStar(currentStarList, tStar, clearedStars);
                }
            }
        }
        if (col + 1 < GameManager02.MAX_COL)
        {
            tStar = Star.LookupStar(currentStarList, row, col + 1);
            if (tStar != null && !Star.IsExisted(clearedStars, tStar))
            {
                if (tStar.tag == star.tag)
                {
                    clearedStars.Add(tStar);
                    FindStar(currentStarList, tStar, clearedStars);
                }
            }
        }
        if (row - 1 >= 0)
        {
            tStar = Star.LookupStar(currentStarList, row - 1, col);
            if (tStar != null && !Star.IsExisted(clearedStars, tStar))
            {
                if (tStar.tag == star.tag)
                {
                    clearedStars.Add(tStar);
                    FindStar(currentStarList, tStar, clearedStars);
                }
            }
        }
        if (row + 1 < GameManager02.MAX_ROW)
        {
            tStar = Star.LookupStar(currentStarList, row + 1, col);
            if (tStar != null && !Star.IsExisted(clearedStars, tStar))
            {
                if (tStar.tag == star.tag)
                {
                    clearedStars.Add(tStar);
                    FindStar(currentStarList, tStar, clearedStars);
                }
            }
        }
    }
   
               c.皮肤的切换,更换图片的Sprite就好了




     /**
     *修改皮肤的方法
     */
    private void ChangeSkin()
    {
        if (PlayerPrefs.GetInt("SkinMode") == 0)
        {
            background.GetComponent<Image>().sprite = skinSprites[0];

            redStarPrefab.GetComponent<Image>().sprite = starSprites[0];
            blueStarPrefab.GetComponent<Image>().sprite = starSprites[1];
            yellowStarPrefab.GetComponent<Image>().sprite = starSprites[2];
            purpleStarPrefab.GetComponent<Image>().sprite = starSprites[3];
            greenStarPrefab.GetComponent<Image>().sprite = starSprites[4];
        }

        if (PlayerPrefs.GetInt("SkinMode") == 1)
        {
            background.GetComponent<Image>().sprite = skinSprites[1];

            redStarPrefab.GetComponent<Image>().sprite = starSprites[5];
            blueStarPrefab.GetComponent<Image>().sprite = starSprites[6];
            yellowStarPrefab.GetComponent<Image>().sprite = starSprites[7];
            purpleStarPrefab.GetComponent<Image>().sprite = starSprites[8];
            greenStarPrefab.GetComponent<Image>().sprite = starSprites[9];
        }
    }
               d.星星消除后的位置移动
                         上下的移动,核心算法就不介绍了,简单易懂。
     /**
     * 更新上下,并且返回空列的col的集合,以便进行左右的移动
     */
    public static List<int> UpdateUD()
    {
        List<int> nullColumn = new List<int>();

        for (int i = 0; i < GameManager02.MAX_COL; i++)
        {
            int moveStep = 0;
            for (int j = GameManager02.MAX_ROW - 1; j >= 0; j--)
            {

                Star star = Star.LookupStar(StarManager.starList, j, i);
                if (star == null)
                {
                    moveStep++;
                    continue;
                }
                else
                {
                    if (moveStep == 0)
                    {
                        continue;
                    }
                    else
                    {
                        star.row += moveStep;

                        star.SetPos();
                    }
                }
            }

            if (moveStep == GameManager02.MAX_ROW)
            {
                nullColumn.Add(i);
            }
        }

        if (nullColumn != null)
        {
            for (int i = 0; i < nullColumn.Count; i++)
            {
                print(nullColumn + " ");
            }

        }

        return nullColumn;
    }
                         左右的移动,基于上下移动
/**
     *更新左右,传入空列的集合
     */
    public static void UpdateLR(List<int> nullColumn)
    {
        int moveStep = 0;
        for (int i = 0; i < GameManager02.MAX_COL; i++)
        {
            if (nullColumn.Contains(i))
            {
                moveStep++;
            }
            else
            {
                if (moveStep == 0)
                {
                    continue;
                }
                else
                {
                    for (int j = GameManager02.MAX_ROW - 1; j >= 0; j--)
                    {
                        Star star = Star.LookupStar(StarManager.starList, j, i);
                        if (star != null)
                        {
                            star.col -= moveStep;

                            star.SetPos();
                        }

                    }
                }
            }
        }
    }
游戏逻辑的实现都在Star脚本里面完成,鼠标点击开始时调用函数。
到了这一步游戏的主要功能就差不多完成了。
二,分数计算的实现,以及存档的保存
          根据一次消除星星的个数进行计算分数:10*(Mathf.pow(2,number-2)),最高次定为8
           canvas下新建空物体ScoreManager用于挂载ScoreManager脚本进行分数的计算
public float ComputeScore(int number)
    {
        if (number<8)
        {
            return 10 * Mathf.Pow(2, number);
        }
        else
        {
            return 10 * Mathf.Pow(2, 8);
        }
    }
三,动画的制作和画面的优化
当当前分数>目标分数,通关成功,加在下一关;
反之闯关败,记录分数和关卡,更新最高分等信息并返回主菜单
主动画的制作要是面板显示的的渐入渐出
    /**
     * 根据剩余星星数,给予额外得分
     */
    public void GetBonusScore(List<Star> starList)
    {
        if (starList.Count>20)
        {
            return;
        }
        else
        {
            float bonusScore = 10 * (20 - starList.Count);
            score += bonusScore;
        }
        UpdateScoreText();
    }
   
    /**
     *根据关卡数计算目标分数
     */
    public void GetGoalScore(int number)
    {
        goalScore = 1000 * number + 2000 * (number / 3 + 1);
    }

    /**
     *更新UI
     */   
    public void UpdateScoreText()
    {
        scoreText.text = "当前分数:" + score;
        goalScoreText.text = "目标分数:" + goalScore;
        levelText.text = "当前关卡:" + level;
    }

    public void GetBounsUI(int number)
    {
        float bonusScore = 10 * (20 - number);
        bouns.gameObject.SetActive(true);
        bouns.text = "剩余:" + number + "  额外得分" + bonusScore;
    }
5.音效的添加和粒子系统的完善。更改渲染模式为world space,手残党。粒子特效太硬核了,就演示了。
6.导出游戏,工程结束。


一小段演示视屏:


https://www.zhihu.com/video/1131908748255662080

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-2-12 09:35 | 显示全部楼层
发表于 2023-2-12 09:44 | 显示全部楼层
为啥劝退u3d?
发表于 2023-2-12 09:51 | 显示全部楼层
[赞][赞][赞]
发表于 2023-2-12 09:51 | 显示全部楼层
好!!!
发表于 2023-2-12 09:57 | 显示全部楼层
哈哈,听起来好玩而已,没其他意思
发表于 2023-2-12 10:04 | 显示全部楼层
楼主可以分享一下源代码吗?正好要学习制作消灭星星,有许多不懂的问题
发表于 2023-2-12 10:07 | 显示全部楼层
核心的代码基本上都在里面了
发表于 2023-2-12 10:08 | 显示全部楼层
LookupStar是什么方法?
发表于 2023-2-12 10:16 | 显示全部楼层
同问 LookupStar是什么方法?
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-2 00:40 , Processed in 0.435190 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表