当前位置: 首页 > news >正文

Unity中解锁图片像素点,动态闭合轨迹检测

Unity中解锁图片像素点,动态闭合轨迹检测

  • 介绍
  • 资源下载
  • 搭建
  • 总结

介绍

因为最近在研究Mane天蚕变的游戏完整逻辑,研究了两套方案做解锁图片的功能,这里我先讲一下我的这个图片像素点的方案解锁图片,这个逻辑其实很简单就是利用划线检测是否轨迹点闭合,然后闭合的点在映射到图片的像素中即可,当然这里需要制作一个shader就是主图层和遮罩层,因为遮罩层的图是动态生成的,所以这块你的像素越大则性能消耗越多也就会卡顿,所以根据实际情况去考量项目需求。

资源下载

项目资源

请添加图片描述

搭建

Canvas模式
在这里插入图片描述
相机模式
在这里插入图片描述
ImageMask为空物体
LineRenderer为对应组件的物体
GameObject为挂载我自定义脚本的对象
RawImage是我需要挂载Shader材质球的对象,同时也需要将图放上去
在这里插入图片描述
在这里插入图片描述
代码
UnlockImageWithShader运行脚本

using UnityEngine;
using UnityEngine.UI;

public class UnlockImageWithShader : MonoBehaviour
{
    public RawImage imageToUnlock; // 需要解锁的图片(使用RawImage)
    public LineRenderer lineRenderer; // 用于绘制曲线的LineRenderer
    public Shader unlockShader; // 自定义Shader

    private Texture2D maskTexture; // 遮罩纹理
    private Material unlockMaterial; // 使用Shader的材质

    void Start()
    {
        // 初始化遮罩纹理
        maskTexture = new Texture2D(256, 256); // 根据需要设置分辨率
        maskTexture.filterMode = FilterMode.Point;
        maskTexture.wrapMode = TextureWrapMode.Clamp;
        ClearMaskTexture(); // 初始化为全黑

        unlockMaterial = new Material(unlockShader);
        unlockMaterial.SetTexture("_MainTex", imageToUnlock.texture); // 设置主纹理
        unlockMaterial.SetTexture("_MaskTex", maskTexture); // 设置遮罩纹理
        imageToUnlock.material = unlockMaterial; // 应用材质到RawImage

        // 初始化LineRenderer
        lineRenderer.startWidth = 0.05f;
        lineRenderer.endWidth = 0.05f;
        lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
        lineRenderer.positionCount = 0;
    }

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            // 将鼠标屏幕坐标转换为UV坐标
            TVector mousePos = GetMouseUVPosition();
            points = AddPointUV(mousePos.m_uv);
            linePoints = AddPointWP(mousePos.m_worldPos);
            lineRenderer.positionCount = points.Length;
            lineRenderer.SetPositions(System.Array.ConvertAll(linePoints, v => v));
        }

        if (Input.GetMouseButtonUp(0))
        {
            if (IsClosedCurve(points))
            {
                UnlockArea(points);
            }
            points = new Vector2[0]; // 清空点数组
            linePoints = new Vector3[0];
            lineRenderer.positionCount = 0; // 清空LineRenderer
        }
    }

    private Vector2[] points = new Vector2[0]; // 存储玩家绘制的点
    private Vector3[] linePoints = new Vector3[0]; // 存储玩家绘制的点

    Vector2[] AddPointUV(Vector2 point)
    {
        Vector2[] newPoints = new Vector2[points.Length + 1];
        points.CopyTo(newPoints, 0);
        newPoints[points.Length] = point;
        return newPoints;
    }

    Vector3[] AddPointWP(Vector3 point)
    {
        Vector3[] newPoints = new Vector3[points.Length + 1];
        linePoints.CopyTo(newPoints, 0);
        newPoints[points.Length] = point;
        return newPoints;
    }

    bool IsClosedCurve(Vector2[] points)
    {
        if (points.Length < 3) return false; // 至少需要3个点才能形成闭合曲线
        float distance = Vector2.Distance(points[0], points[points.Length - 1]);
        return distance < 0.05f; // 判断起点和终点是否接近(阈值)
    }

    void UnlockArea(Vector2[] points)
    {

        // 将多边形区域填充为白色
        for (int y = 0; y < maskTexture.height; y++)
        {
            for (int x = 0; x < maskTexture.width; x++)
            {
                Vector2 pixelUV = new Vector2((float)x / maskTexture.width, (float)y / maskTexture.height);
                if (IsPointInPolygon(pixelUV, points))
                {
                    Color c = new Color(Color.white.r, Color.white.g, Color.white.b, 1);
                    maskTexture.SetPixel(x, y, c); // 设置为白色(解锁)
                }
            }
        }
        maskTexture.Apply(); // 应用纹理更改

        byte[] b = maskTexture.EncodeToPNG();
        System.IO.File.WriteAllBytes(Application.dataPath + "/t1.png", b);

        Debug.LogError("Mask texture updated."); // 调试日志
    }

    bool IsPointInPolygon(Vector2 point, Vector2[] polygon)
    {
        int intersections = 0;
        for (int i = 0; i < polygon.Length; i++)
        {
            Vector2 p1 = polygon[i];
            Vector2 p2 = polygon[(i + 1) % polygon.Length];

            if (point.y > Mathf.Min(p1.y, p2.y))
            {
                if (point.y <= Mathf.Max(p1.y, p2.y))
                {
                    if (point.x <= Mathf.Max(p1.x, p2.x))
                    {
                        float xIntersection = (point.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
                        if (p1.x == p2.x || point.x <= xIntersection)
                        {
                            intersections++;
                        }
                    }
                }
            }
        }
        return intersections % 2 != 0; // 奇数交点表示点在多边形内
    }

    public class TVector 
    {
        public Vector2 m_uv;
        public Vector3 m_worldPos;
        public TVector(Vector2 uv,Vector3 wp)
        {
            m_uv = uv;
            m_worldPos = wp;
        }
    }

    TVector GetMouseUVPosition()
    {
        // 将鼠标屏幕坐标转换为UV坐标
        Vector2 mousePos = Input.mousePosition;
        // 确保传入的摄像机参数正确
        bool success = RectTransformUtility.ScreenPointToLocalPointInRectangle(
            imageToUnlock.rectTransform, mousePos, Camera.main, out Vector2 localPoint);

        RectTransformUtility.ScreenPointToWorldPointInRectangle(
    imageToUnlock.rectTransform, mousePos, Camera.main, out Vector3 worldPos);

        // 将局部坐标转换为 UV 坐标
        Vector2 uv = Rect.PointToNormalized(imageToUnlock.rectTransform.rect, localPoint);

        return new  TVector (uv, worldPos);
    }

    void ClearMaskTexture()
    {
        // 将遮罩纹理初始化为全黑
        Color[] colors = new Color[maskTexture.width * maskTexture.height];
        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = new Color(Color.black.r, Color.black.g, Color.black.b, 0);
        }
        maskTexture.SetPixels(colors);
        maskTexture.Apply();
    }
}

UnlockImageShader.Shader

Shader "Custom/UnlockImageShader"
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white" {} // 主纹理
        _MaskTex ("Mask Texture", 2D) = "white" {} // 遮罩纹理
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Transparent" }
        LOD 200

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            sampler2D _MaskTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 mainColor = tex2D(_MainTex, i.uv); // 主纹理颜色
                fixed4 maskColor = tex2D(_MaskTex, i.uv); // 遮罩纹理颜色

                // 如果遮罩纹理的 alpha 值大于 0.5,显示主纹理,否则显示黑色
                return maskColor.a > 0.5 ? mainColor : fixed4(0, 0, 0, 1);
            }
            ENDCG
        }
    }
}

总结

需要的同学们可以去我上面资源下载的位置去下载。
感谢大家的支持!

相关文章:

  • 实验三 Python 数据可视化 Python 聚类-K-means(CQUPT)
  • C语言从入门到精通
  • 征程 6 基于 Linux 和 Node-Locked License 配置 DSP 开发环境
  • 【WEB APIs】DOM-事件基础
  • 【服务器知识】Nginx路由匹配规则说明
  • 详解SQL数据更新功能
  • C# 集合
  • 建筑管理(2): 施工承包模式,工程监理,质量监督
  • 最完美的WPF无边框设计!
  • Java 大视界 -- Java 大数据分布式计算中的资源调度与优化策略(131)
  • Java 代理模式:从静态代理到动态代理
  • DeepSeek:为教培小程序赋能,引领行业变革新潮流
  • 基于STM32的火灾报警设备(阿里云平台)
  • Oracle Database 11g、12c、18c、19c、21c、22c 与 23AI 各版本差异、优缺点详解
  • 【Node.js入门笔记6---fs流(Streams)与管道(Pipe)】
  • 使用 Doris 和 Hudi
  • JVM 如何保证 Java 程序的安全性?
  • 共享内存的通信
  • css模拟雷达扫描动画
  • 新办公室哪款空气净化器除甲醛效果好?高效除甲醛,提升效率
  • 蔡建忠已任昆山市副市长、市公安局局长
  • 第十届曹禺剧本奖上海揭晓,首次开放个人申报渠道
  • 首次公布!我国空间站内发现微生物新物种
  • 广西壮族自治区党委常委会:坚决拥护党中央对蓝天立进行审查调查的决定
  • 梅花奖在上海|话剧《主角》:艺术与人生的交错
  • 李成钢出席中国与《数字经济伙伴关系协定》成员部级会议