Unity中实现简单刮刮乐效果

Unity实现刮刮乐效果

本文转自:http://www.manew.com/thread-113128-1-1.html,请点击链接查看原文,尊重楼主版权。

网上看过了很多刮刮乐的文章,自己参考了一些,也改良了一些方法,比如说改良了画的每个点不连续的情况。具体效果如下:

做出这种效果,其实挺简单,主要就是利用unity的render texture加上自己写的遮罩shader。
我们首先设置两个摄像机,一个是专门渲染render texture用的,让它只能看到笔刷图层,命名为brushCamera,并且要设为dont clear模式:

然后我们要创建一个笔刷预设体,这个笔刷预设体主要实现笔刷效果:

下面的实现思路就是:
当按下鼠标时,我们就克隆一个笔刷,这样就形成了涂画的效果。
然后我们写一个遮罩shader,shader中需要两张图,一张是遮罩的图片(就是图中的蓝色图片),另一张是用于剔除遮罩的图片,我们将渲染出的rendertexture作为剔除遮罩的图片。这样就完成了刮刮乐效果。

其中,我们要注意几个问题:
1、由于当鼠标快速滑动时,可能会产生每个点不连续的情况,这里我们用了贝塞尔平滑方法进行处理。
2、大量克隆笔刷,会非常消耗性能,这里我们采用创建对象池方法的方法解决这个问题。

下面是主要的代码:
c#代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DrawMask : MonoBehaviour {
public float radius = 0.5f;//半径
public GameObject brush;
bool startDraw = false;
bool twoPoints = false;
Vector2 lastPos;//最后一个点
Vector2 penultPos;//倒数第二个点
List<GameObject> brushesPool = new List<GameObject>(),activeBrushes = new List<GameObject>();//笔刷对象池

public delegate void DrawHandler(Vector2 pos);
public event DrawHandler onStartDraw;
public event DrawHandler onEndDraw;
public event DrawHandler drawing;

void Update () {
 GetInput();
}

void GetInput()
{
if (Input.GetMouseButtonDown(0))
{
startDraw = true;
if (onStartDraw != null)
{
onStartDraw(VectorTransfer(Input.mousePosition));
}
penultPos = Input.mousePosition;
}
else if (Input.GetMouseButton(0))
{
if (twoPoints && Vector2.Distance(Input.mousePosition,lastPos) > 0.5f)//如果两次记录的鼠标坐标距离大于一定的距离,开始记录鼠标的点
{
Vector2 pos = Input.mousePosition;
float dis = Vector2.Distance(lastPos, pos);
int segments = (int)(dis / radius);//计算出平滑的段数
segments = segments < 1 ? 1 : segments;
Vector2[] points = Beizier(penultPos, lastPos, pos, segments);//进行贝塞尔平滑
for (int i = 0; i < points.Length; i++)
{
InstanceBrush(VectorTransfer(points[i]));
}
if (drawing != null)
{
drawing(VectorTransfer(Input.mousePosition));
}
lastPos = pos;
penultPos = points[points.Length - 2];
}
else
{
twoPoints = true;
lastPos = Input.mousePosition;
}
}
else if (Input.GetMouseButtonUp(0))
{
if (onEndDraw != null)
{
onEndDraw(VectorTransfer(Input.mousePosition));
}
startDraw = false;
twoPoints = false;
}
}

private void OnPostRender()
{
InitBrushes();
}

void InitBrushes()
{
for (int i = 0; i < activeBrushes.Count; i++)
{
activeBrushes[i].SetActive(false);
brushesPool.Add(activeBrushes[i]);
}
activeBrushes.Clear();
}

void InstanceBrush(Vector2 pos)
{
GameObject brushClone;
if (brushesPool.Count > 0)
{
brushClone = brushesPool[brushesPool.Count - 1];
brushesPool.RemoveAt(brushesPool.Count - 1);
}
else
{
brushClone = Instantiate(brush, pos, Quaternion.identity);
}
brushClone.transform.position = pos;

brushClone.transform.localScale = Vector3.one * radius;
brushClone.SetActive(true);
activeBrushes.Add(brushClone);
}

/// <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(); 
}

Vector2 VectorTransfer(Vector2 point)
{
return Camera.main.ScreenToWorldPoint(new Vector3(point.x, point.y, 0));
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

遮罩shader:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/MaskShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
//_MainTex ("Albedo (RGB)", 2D) = "white" {}
_MaskTex("Mask Texture",2D) = "white"{}
_Mask("Mask",2D) = "white"{}

}
SubShader {
Tags{"RenderType" = "Transparent" "Queue" = "Transparent"}
pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "unitycg.cginc"

struct v2f 
{
float4 pos:POSITION;
float2 uv:TEXCOORD1;
};

//sampler2D _MainTex;
sampler2D _MaskTex;
sampler2D _Mask;

v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}

float4 frag(v2f i):COLOR
{
//float4 mainColor = tex2D(_MainTex,i.uv);
float4 maskTexColor = tex2D(_MaskTex,i.uv);
float4 maskColor = tex2D(_Mask,i.uv);
maskTexColor.a = 1 - maskColor.a;
return maskTexColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

项目源码:请点击原链接查看楼主原文,尊重楼主版权,

猜你喜欢

转载自blog.csdn.net/qq_38112703/article/details/80483483