7

❤️只用一个脚本做一个 刮刮乐 案例,一不小心刮出来一个女朋友!❤️【学习娱乐一下】

 2 years ago
source link: https://blog.csdn.net/zhangay1998/article/details/120047305
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 📢博客主页:https://blog.csdn.net/zhangay1998
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉
  • 📢未来很长,值得我们全力奔赴更美好的生活✨

请添加图片描述


  • 前几天CSDN一个大哥:孤寒者 使用HTML做了一个刮刮乐,大家都挺喜欢看的!

  • 我也感觉挺好玩的,那就寻思用 Unity 肯定也能做呀! Unity 是一个容易上手的游戏引擎

  • 做出来之后可以直接打包出来给别的 小哥哥小姐姐 直接在手机上玩都行!!

  • 那就来简单说一下怎样实现这样一个刮刮乐的小案例吧,只需要一个脚本 就能完成~

  • 本文章会从一个新手的角度完整的将全过程都会写出来!老手直接看脚本挂上即可!

  • 如果之前没接触过 Unity 也没关系,这正是你入坑的一个机会!Unity免费学习专栏🎄 Unity基础知识学习 🎄


🎬只用一个脚本做一个刮刮乐,一不小心刮出来一个女朋友!

请添加图片描述


🏳️‍🌈第一步:打开Unity,新建一个项目

既然我们是用 Unity 完成这个小项目,第一步就来新建一个项目!

如果你连 Unity 引擎是什么都不知道的话,那真的有点落伍啦~

这里推荐一个免费的 Unity 学习专栏,包括 怎样下载安装 Unity 和 配置基础环境 !

以最简单的一个视角了解 什么是Unity 和 Unity最基础知识学习!

打开UnityHub新建一个项目,改一下项目名称路径,然后点击创建
在这里插入图片描述

创建完之后,就会显示出一个默认的Unity空场景,可能窗口显示的位置不太一样,自己可以随意调整,这个都不重要~
在这里插入图片描述


🏳️‍🌈第二步:新建一个Image图片和RawImage图片

在Hierarchy面板上右键创建一个Image和一个RawImage

如下图所示,只需要明白这两个Image组件都是用来显示图片的组件就OK啦~ 想学习更多可以去专栏学习哦!
在这里插入图片描述
然后将这两个Image调整成跟Canvas一个大小!

先点击Image,选中之后,将图片调整成Canvas一个大小,按照下图所示快捷操作即可

也可以手动的将Image图片调整成跟Canvas一个大小!
在这里插入图片描述
Image和RawImage都一样设置一下,然后场景中变成下面这样!
在这里插入图片描述


🏳️‍🌈第三步:调整相机和画布的模式

先选中相机Camera,将相机的投影模式改成 正交,就是改一下这个Projection设置,如下图所示!
在这里插入图片描述
然后再选中Canvas画布,改成画布的第二种模式,如下所示:
在这里插入图片描述
Canvas画布概念如下:
在这里插入图片描述
然后还没完,还要将Canvas画布的缩放模式改为第二种

按照屏幕大小缩放,把分辨率调成1920*1080,然后把相机Main Camera挂载到这个Canvas上面!

如下所示
在这里插入图片描述
在这里插入图片描述


🏳️‍🌈第四步:写一个脚本,并挂载到Canvas上

上面的几步都是为了将图片显示的时候更加美观到位 ,这一步才是这个刮刮乐最关键的一步!

新建一个C#脚本,然后将以下代码复制到脚本中即可
在这里插入图片描述

直接上代码

using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Demo: MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
    //是否擦除了
    public bool isStartEraser;
    //是否擦除结束了
    public bool isEndEraser;
    //开始事件
    public Action eraserStartEvent;
    //结束事件
    public Action eraserEndEvent;
    public RawImage uiTex;
    Texture2D tex;
    Texture2D MyTex;
    int mWidth;
    int mHeight;
    [Header("笔刷大小")]
    public int brushSize = 50;
    [Header("刮刮乐比例")]
    public int rate = 90;
    float maxColorA;
    float colorA;
    void Awake()
    {
        tex = (Texture2D)uiTex.mainTexture;
        MyTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
        mWidth = MyTex.width;
        mHeight = MyTex.height;
        MyTex.SetPixels(tex.GetPixels());
        MyTex.Apply();
        uiTex.texture = MyTex;
        maxColorA = MyTex.GetPixels().Length;
        colorA = 0;
        isEndEraser = false;
        isStartEraser = false;
    }
    /// <summary>
    /// 贝塞尔平滑
    /// </summary>
    /// <param name="start">起点</param>
    /// <param name="mid">中点</param>
    /// <param name="end">终点</param>
    /// <param name="segments">段数</param>
    /// <returns></returns>
    public Vector2[] Beizier(Vector2 start, Vector2 mid, Vector2 end, int segments)
    {
        float d = 1f / segments;
        Vector2[] points = new Vector2[segments - 1];
        for (int i = 0; i < points.Length; i++)
        {
            float t = d * (i + 1);
            points[i] = (1 - t) * (1 - t) * mid + 2 * t * (1 - t) * start + t * t * end;
        }
        List<Vector2> rps = new List<Vector2>();
        rps.Add(mid);
        rps.AddRange(points);
        rps.Add(end);
        return rps.ToArray();
    }
    bool startDraw = false;
    bool twoPoints = false;
    Vector2 lastPos;//最后一个点
    Vector2 penultPos;//倒数第二个点
    float radius = 12f;
    float distance = 1f;
    #region 事件
    public void OnPointerDown(PointerEventData eventData)
    {
        if (isEndEraser) { return; }
        startDraw = true;
        penultPos = eventData.position;
        CheckPoint(penultPos);
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (isEndEraser) { return; }
        if (twoPoints && Vector2.Distance(eventData.position, lastPos) > distance)//如果两次记录的鼠标坐标距离大于一定的距离,开始记录鼠标的点
        {
            Vector2 pos = eventData.position;
            float dis = Vector2.Distance(lastPos, pos);

            CheckPoint(eventData.position);
            int segments = (int)(dis / radius);//计算出平滑的段数                                              
            segments = segments < 1 ? 1 : segments;
            if (segments >= 10) { segments = 10; }
            Vector2[] points = Beizier(penultPos, lastPos, pos, segments);//进行贝塞尔平滑
            for (int i = 0; i < points.Length; i++)
            {
                CheckPoint(points[i]);
            }
            lastPos = pos;
            if (points.Length > 2)
                penultPos = points[points.Length - 2];
        }
        else
        {
            twoPoints = true;
            lastPos = eventData.position;
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (isEndEraser) { return; }
        //CheckPoint(eventData.position);
        startDraw = false;
        twoPoints = false;
    }


    #endregion
    void CheckPoint(Vector3 pScreenPos)
    {
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(pScreenPos);
        Vector3 localPos = uiTex.gameObject.transform.InverseTransformPoint(worldPos);

        if (localPos.x > -mWidth / 2 && localPos.x < mWidth / 2 && localPos.y > -mHeight / 2 && localPos.y < mHeight / 2)
        {
            for (int i = (int)localPos.x - brushSize; i < (int)localPos.x + brushSize; i++)
            {
                for (int j = (int)localPos.y - brushSize; j < (int)localPos.y + brushSize; j++)
                {
                    if (Mathf.Pow(i - localPos.x, 2) + Mathf.Pow(j - localPos.y, 2) > Mathf.Pow(brushSize, 2))
                        continue;
                    if (i < 0) { if (i < -mWidth / 2) { continue; } }
                    if (i > 0) { if (i > mWidth / 2) { continue; } }
                    if (j < 0) { if (j < -mHeight / 2) { continue; } }
                    if (j > 0) { if (j > mHeight / 2) { continue; } }

                    Color col = MyTex.GetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2);
                    if (col.a != 0f)
                    {
                        col.a = 0.0f;
                        colorA++;
                        MyTex.SetPixel(i + (int)mWidth / 2, j + (int)mHeight / 2, col);
                    }
                }
            }
            //开始刮的时候 去判断进度
            if (!isStartEraser)
            {
                isStartEraser = true;
                InvokeRepeating("getTransparentPercent", 0f, 0.2f);
                if (eraserStartEvent != null)
                    eraserStartEvent.Invoke();
            }
            MyTex.Apply();
        }
    }
    double fate;
    /// <summary> 
    /// 检测当前刮刮卡 进度
    /// </summary>
    /// <returns></returns>
    public void getTransparentPercent()
    {
        if (isEndEraser) { return; }
        fate = colorA / maxColorA * 100;
        fate = (float)Math.Round(fate, 2);
         Debug.Log("当前百分比: " + fate);
        if (fate >= rate)
        {
            isEndEraser = true;
            CancelInvoke("getTransparentPercent");
            uiTex.gameObject.SetActive(false);
            //触发结束事件
            if (eraserEndEvent != null)
                eraserEndEvent.Invoke();
        }
    }
}

代码中有注释,新手的话直接挂上即可,不需要管什么代码释义,毕竟编程的真谛就是“CV大法”哈哈哈哈嗝~

代码中使用一个贝塞尔平滑的方法可以让待会刮刮乐的时候更平滑一些,不至于出现撕裂齿轮等等现象!

然后利用鼠标按下后的两个点的点击距离,计算出平滑的段数然后使用贝塞尔平滑进行刮刮乐效果~

最后再写一个方法,对完成刮刮乐的一个进度来进行判断,当超过一定比例后显示整张图片!

将脚本挂到Canvas画布上,然后将RawImage拖到这个脚本中,RawImage就是我们刮刮乐上面的那层图片

刮完之后显示的图片用Image,然后简单设置一下笔刷的大小可以控制刮的时候的大小,效果如下:
在这里插入图片描述
然后就是。。找两张惊艳的图片用来当做刮刮乐的上层和下层图片进行展示吧~


🏳️‍🌈最后一步:找两张图片放到场景中!

这一步说是最简单的一步,其实也挺难的!

难的地方是:要找怎样的图片才能吸引到CSDN看到这篇博客的各位精英呢!

我这里实在是没什么超级惊艳的美图来展示,那就随便选两张吧~

然后点击场景中的Image 和RawImage ,分别将图片挂载上去即可!

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


🎬效果展示

然后运行 Unity 进行刮刮乐就好了!千万不要刮上瘾了哦!

注意成年人防沉迷模式!

请添加图片描述
原图奉上!

刮刮乐上层图片:
请添加图片描述
刮刮乐下层图片:
在这里插入图片描述


  • 本文到此就结束啦, 主要写了一个刮刮乐的案例
  • 因为是从一个新手角度出发,所以每个步骤都多说了一点,方便使用和学习
  • 如果你对这个案例挺感兴趣或者对Unity挺感兴趣,可以三连一波关注我的博客
  • 后面还会分享更多的实用好玩的案例学习和小游戏学习哦!
  • 如果你都看到这里了,真的可以自己尝试一下做一些小游戏玩,上手挺简单的!
  • 非常适合大学生宿舍无聊的时候做小游戏哦~

🚀往期优质文章分享


🚀 优质专栏分享 🚀
  • 🎄如果感觉文章看完了不过瘾,可以来我的其他 专栏 看一下哦~
  • 🎄比如以下几个专栏:Unity基础知识学习专栏、Unity游戏制作专栏、Unity实战类项目 和 算法学习专栏
  • 🎄可以学习更多的关于Unity引擎的相关内容哦!直接点击下面颜色字体就可以跳转啦!
🎄 Unity基础知识学习专栏 🎄

⭐️ Unity游戏制作专栏 ⭐️ 🍇 Unity实战类项目 🍇 💦 小Y学算法 💦


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK