Unity标记固定颜色及投影标记

1 结构

2 CameraDisplay和DrawingOverlay为Raw Image

3 ProjectorCanvas可以直接复制上面的Canvas,然后删除其他不用的。

最后留下的只是渲染原来的标记。

4 游戏组件设置

Main Camera

ProjectorCamera

Canvas

cs 复制代码
    using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class FixedOneColor : MonoBehaviour
{
    [Header("底层显示摄像头")]
    public RawImage CamRaw;
    [Header("顶层绘图面板(同尺寸全屏)")]
    public RawImage DrawRaw;

    [Header("投影端RawImage")]
    public RawImage projectorRaw;

    private WebCamTexture _camTex;
    private Texture2D _drawTex;
    private int _w, _h;
    private bool _isDraw;
    private List<Vector2> _curPoints = new List<Vector2>();
    private List<Stroke> _history = new List<Stroke>();

    // 当前画笔颜色,默认红色
    private Color _currentColor = Color.red;

    [Serializable]
    class Stroke
    {
        public List<Vector2> pts = new List<Vector2>();
        public Color col;
    }

    void Start()
    {
        // 初始化相机
        var device = WebCamTexture.devices[0];
        _camTex = new WebCamTexture(device.name, 1280, 720);
        _camTex.Play();
        CamRaw.texture = _camTex;
        // 初始化顶层绘图纹理
        Invoke(nameof(InitDrawTex), 0.5f);
    }

    void InitDrawTex()
    {
        _w = _camTex.width;
        _h = _camTex.height;
        _drawTex = new Texture2D(_w, _h, TextureFormat.RGBA32, false);
        _drawTex.wrapMode = TextureWrapMode.Clamp;
        ClearDrawTex();
        DrawRaw.texture = _drawTex;

        if (projectorRaw != null)
            projectorRaw.texture = _drawTex;
    }

    void Update()
    {
        if (_drawTex == null) return;

        // 键盘快捷键切换画笔颜色(与按钮功能一致)
        if (Input.GetKeyDown(KeyCode.R)) SetRedColor();
        if (Input.GetKeyDown(KeyCode.G)) SetGreenColor();
        if (Input.GetKeyDown(KeyCode.B)) SetBlueColor();

        // 原有快捷键
        if (Input.GetKeyDown(KeyCode.C)) ClearAll();
        if (Input.GetKeyDown(KeyCode.S)) SaveImg();

        DealMouse();
        RenderDraw();
    }

    // ========== 三个颜色切换方法,供UI按钮点击调用 ==========
    public void SetRedColor()
    {
        _currentColor = Color.red;
    }

    public void SetGreenColor()
    {
        _currentColor = Color.green;
    }

    public void SetBlueColor()
    {
        _currentColor = Color.blue;
    }

    void DealMouse()
    {
        // 按下
        if (Input.GetMouseButtonDown(0))
        {
            _isDraw = true;
            _curPoints.Clear();
            _curPoints.Add(GetMouseTexPos());
        }
        // 拖动
        if (Input.GetMouseButton(0) && _isDraw)
        {
            var p = GetMouseTexPos();
            if (Vector2.Distance(p, _curPoints.Last()) > 2f)
                _curPoints.Add(p);
        }
        // 抬起
        if (Input.GetMouseButtonUp(0) && _isDraw)
        {
            _isDraw = false;
            if (_curPoints.Count < 2)
            {
                _curPoints.Clear();
                return;
            }
            // 使用当前选中的颜色创建新笔画,默认红色
            _history.Add(new Stroke { pts = new List<Vector2>(_curPoints), col = _currentColor });
            _curPoints.Clear();
        }
    }

    // 鼠标屏幕坐标 → 纹理像素坐标
    Vector2 GetMouseTexPos()
    {
        RectTransform rect = DrawRaw.rectTransform;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, Input.mousePosition, null, out Vector2 local);
        float x = Mathf.Lerp(0, _w, (local.x / rect.rect.width) + 0.5f);
        float y = Mathf.Lerp(0, _h, (local.y / rect.rect.height) + 0.5f);
        x = Mathf.Clamp(x, 0, _w - 1);
        y = Mathf.Clamp(y, 0, _h - 1);
        return new Vector2(x, y);
    }

    // 渲染所有笔画
    void RenderDraw()
    {
        ClearDrawTex();
        // 历史正式线条
        foreach (var s in _history)
            DrawLineList(s.pts, s.col);
        // 当前临时预览线条,使用当前选中颜色
        if (_isDraw && _curPoints.Count > 1)
            DrawLineList(_curPoints, _currentColor);
        _drawTex.Apply();
    }

    void DrawLineList(List<Vector2> pts, Color col)
    {
        for (int i = 0; i < pts.Count - 1; i++)
            DrawBresenham(pts[i], pts[i + 1], col, 3);
    }

    // 画线
    void DrawBresenham(Vector2 p1, Vector2 p2, Color col, int size)
    {
        int x0 = (int)p1.x, y0 = (int)p1.y;
        int x1 = (int)p2.x, y1 = (int)p2.y;
        int dx = Mathf.Abs(x1 - x0);
        int dy = Mathf.Abs(y1 - y0);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;
        while (true)
        {
            DrawPoint(x0, y0, col, size);
            if (x0 == x1 && y0 == y1) break;
            int e2 = 2 * err;
            if (e2 > -dy) { err -= dy; x0 += sx; }
            if (e2 < dx) { err += dx; y0 += sy; }
        }
    }

    void DrawPoint(int x, int y, Color col, int r)
    {
        for (int dx = -r; dx <= r; dx++)
            for (int dy = -r; dy <= r; dy++)
            {
                if (dx * dx + dy * dy <= r * r)
                {
                    int px = x + dx;
                    int py = y + dy;
                    if (px >= 0 && px < _w && py >= 0 && py < _h)
                        _drawTex.SetPixel(px, py, col);
                }
            }
    }

    void ClearDrawTex()
    {
        if (_drawTex == null) return;
        Color[] clear = new Color[_w * _h];
        for (int i = 0; i < clear.Length; i++)
            clear[i] = Color.clear;
        _drawTex.SetPixels(clear);
    }

    void ClearAll()
    {
        _history.Clear();
        _curPoints.Clear();
        _isDraw = false;
        ClearDrawTex();
    }

    void SaveImg()
    {
        RenderTexture rt = new RenderTexture(_w, _h, 24);
        rt.Create();
        CamRaw.texture = _camTex;
        DrawRaw.texture = _drawTex;
        Texture2D saveTex = new Texture2D(_w, _h);
        saveTex.ReadPixels(new Rect(0, 0, _w, _h), 0, 0);
        saveTex.Apply();
        byte[] png = saveTex.EncodeToPNG();
        string path = Application.persistentDataPath + "/cam_draw.png";
        System.IO.File.WriteAllBytes(path, png);
        Debug.Log("保存成功:" + path);
    }

    void OnDestroy()
    {
        if (_camTex != null) _camTex.Stop();
    }
}

ProjectorCanvas

ProjectorCanvas下面的DrawingOverlay直接复制上在的

效果,主屏可以显示而且投影可以显示标记。

相关推荐
魔士于安4 天前
unity 丑陋哥布林 哥布林有humannoid 动画没 带动画
unity·游戏引擎·材质·贴图·模型
小贺儿开发5 天前
Unity 家居视频遥控(细节优化)
科技·unity·程序员·udp·视频·工具·通信
淡海水5 天前
13-04-面试-源码级深度追问链
数据结构·unity·面试·c#·游戏引擎·源码·il2cpp
小小数媒成员5 天前
UGUI 性能优化(详细,全,深入)
开发语言·unity·游戏引擎
林川~016 天前
Unity新输入系统使用笔记
unity·input system
小贺儿开发6 天前
Unity 结合百度AI开放平台 手写文字识别
人工智能·科技·学习·unity·云服务·文字识别·手写文字
林川~017 天前
Unity 万能物理检测工具:射线检测 / 范围检测 / 层级过滤 / 编辑器可视化(可直接拿去用)
游戏·unity·c#·射线检测·通用工具
yi碗汤园7 天前
MQTT消息队列遥测传输协议
网络·网络协议·unity
林川~017 天前
Unity 常用 API 与核心类全解:从生命周期到协程,附性能避坑清单(Unity 6 适配)
unity·游戏引擎·常用api
sensen_kiss7 天前
CPT306 Coursework 1 个人游戏作业——坦克大战
unity·游戏程序·游戏策划