- 博客主页:https://blog.csdn.net/zhangay1998
- 欢迎点赞 收藏 ⭐留言 如有错误敬请指正!
- 本文由 呆呆敲代码的小Y 原创,首发于 CSDN
- 未来很长,值得我们全力奔赴更美好的生活✨
前几天CSDN一个大哥:孤寒者 使用HTML做了一个刮刮乐,大家都挺喜欢看的!
我也感觉挺好玩的,那就寻思用 Unity 肯定也能做呀! Unity 是一个容易上手的游戏引擎
做出来之后可以直接打包出来给别的 小哥哥小姐姐 直接在手机上玩都行!!
那就来简单说一下怎样实现这样一个刮刮乐的小案例吧,只需要一个脚本 就能完成~
本文章会从一个新手的角度完整的将全过程都会写出来!老手直接看脚本挂上即可!
如果之前没接触过 Unity 也没关系,这正是你入坑的一个机会!Unity免费学习专栏: Unity基础知识学习
既然我们是用 Unity 完成这个小项目,第一步就来新建一个项目!
如果你连 Unity 引擎是什么都不知道的话,那真的有点落伍啦~
这里推荐一个免费的 Unity 学习专栏,包括 怎样下载安装 Unity 和 配置基础环境 !
以最简单的一个视角了解 什么是Unity 和 Unity最基础知识学习!
打开UnityHub新建一个项目,改一下项目名称和路径,然后点击创建。
创建完之后,就会显示出一个默认的Unity空场景,可能窗口显示的位置不太一样,自己可以随意调整,这个都不重要~
在Hierarchy面板上右键创建一个Image和一个RawImage
如下图所示,只需要明白这两个Image组件都是用来显示图片的组件就OK啦~ 想学习更多可以去专栏学习哦!
然后将这两个Image调整成跟Canvas一个大小!
先点击Image,选中之后,将图片调整成Canvas一个大小,按照下图所示快捷操作即可
也可以手动的将Image图片调整成跟Canvas一个大小!
Image和RawImage都一样设置一下,然后场景中变成下面这样!
先选中相机Camera,将相机的投影模式改成 正交,就是改一下这个Projection设置,如下图所示!
然后再选中Canvas画布,改成画布的第二种模式,如下所示:
Canvas画布概念如下:
然后还没完,还要将Canvas画布的缩放模式改为第二种
按照屏幕大小缩放,把分辨率调成1920*1080,然后把相机Main Camera挂载到这个Canvas上面!
上面的几步都是为了将图片显示的时候更加美观到位 ,这一步才是这个刮刮乐最关键的一步!
直接上代码
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;
}
///
/// 贝塞尔平滑
///
/// 起点
/// 中点
/// 终点
/// 段数
///
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;
///
/// 检测当前刮刮卡 进度
///
///
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 进行刮刮乐就好了!千万不要刮上瘾了哦!
注意成年人防沉迷模式!
原图奉上!
优质专栏分享 |