unity制作刮刮乐效果

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

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


 

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

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

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

[C#]  纯文本查看  复制代码
?
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
136
137
138
139
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
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;
     // Use this for initialization
     void Start () {
     }
     
     // Update is called once per frame
     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));
     }
}



遮罩shader:
[C]  纯文本查看  复制代码
?
 
01
02
03
04
05
06
07
08
09
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
// 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"
}

Unity是一款功能强大的游戏开发引擎,它提供了丰富的工具和功能来制作各种不同的游戏效果,包括立体效果。 首先,Unity提供了灵活的3D建模工具,可以创建各种形状的物体。开发者可以通过其内置的编辑器或者外部的3D建模软件来设计和建立游戏场景中的立体物体。这些物体可以是简单的几何体,也可以是复杂的模型,甚至可以使用粒子系统创建出逼真的特效。 其次,Unity还提供了强大的光照和渲染系统,可以给立体物体添加各种灯光效果,例如阴影、反射和折射等。通过设置不同的光源和材质属性,可以调整立体物体的表面光泽和颜色,使其看起来更加真实和具有立体感。 另外,Unity还支持使用各种不同的着色器(shader)来控制立体物体的外观。着色器可以用来处理立体物体的纹理贴图、法线贴图、透明度、反射等效果,通过调整着色器的参数可以实现各种想要的立体效果。 此外,Unity还提供了强大的物理引擎,可以模拟立体物体之间的碰撞和受力情况。利用物理引擎,开发者可以实现立体物体之间的真实互动,例如球体滚动、物体堆叠等。 总之,Unity提供了丰富的功能和工具,可以轻松地实现立体效果。开发者可以利用其灵活的建模工具、强大的光照和渲染系统、多样的着色器和物理引擎来制作出各种带有立体效果的游戏场景和物体,为玩家呈现出一个更加逼真和立体的游戏世界。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值