返回首页DA系统C#IDE文件同步服务屏保 今天是: 2026-05-05    "立夏"  夏季的第一个节气,表示盛夏时节的正式开始

搜索
热搜: linux 技术
Hi~登录注册
查看: 2534|回复: 0

[转载] 【转载】Unity3D (2022版本亲测可用)制作桌宠核心内容及代码

[复制链接]
发表于 2025-1-2 19:11:48 | 显示全部楼层 |阅读模式
本帖最后由 da11 于 2025-1-2 19:13 编辑

Unity3D (2022版本亲测可用)制作桌宠核心内容及代码


准备工作
1.本帖子示例为使用sprite来制作宠物对象,参考帖子使用的是Live2D人物。
2.原帖子项目是在 unity2018.4.24.f1(很重要) 的环境下开发(已在2022.3.40f1c1版本测试通过,其余版本未测试),操作系统为Windows。


核心中的核心-全屏+背景透明+点击穿透+置顶

导出程序的时候必须使背景透明,否则就谈不上桌宠了。这里直接给出背景透明+点击穿透的代码,将此代码直接拖动给相机Camera,然后把相机的Clear Flags设为Solid Color,并把背景颜色调成黑色。(代码基于这篇博客修改)

#####################################################################

using UnityEngine;

using System.Runtime.InteropServices; // 为了使用DllImport

using System;

// 让程序背景透明

public class TestWindows : MonoBehaviour
{

    private IntPtr hwnd;

    private int currentX;

    private int currentY;



    #region Win函数常量

    private struct MARGINS

    {

        public int cxLeftWidth;

        public int cxRightWidth;

        public int cyTopHeight;

        public int cyBottomHeight;

    }



    [DllImport("user32.dll")]

    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]

    static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]

    static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]

    static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

    [DllImport("Dwmapi.dll")]

    static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);

    [DllImport("user32", EntryPoint = "SetLayeredWindowAttributes")]

    private static extern uint SetLayeredWindowAttributes(IntPtr hwnd, int crKey, int bAlpha, int dwFlags);



    // 定义窗体样式,-16表示设定一个新的窗口风格

    private const int GWL_STYLE = -16;

    //设定一个新的扩展风格

    private const int GWL_EXSTYLE = -20;



    private const int WS_EX_LAYERED = 0x00080000;

    private const int WS_BORDER = 0x00800000;

    private const int WS_CAPTION = 0x00C00000;

    private const int SWP_SHOWWINDOW = 0x0040;

    private const int LWA_COLORKEY = 0x00000001;

    private const int LWA_ALPHA = 0x00000002;

    private const int WS_EX_TRANSPARENT = 0x20;

    #endregion



    void Awake()

    {

        Application.targetFrameRate = 60;

        var productName = Application.productName;

#if !UNITY_EDITOR

        // 获得窗口句柄

        hwnd = FindWindow(null, productName);



        // 设置窗体属性

        int intExTemp = GetWindowLong(hwnd, GWL_EXSTYLE); // 获得当前样式

        SetWindowLong(hwnd, GWL_EXSTYLE, intExTemp | WS_EX_LAYERED); // 当前样式加上WS_EX_LAYERED     // WS_EX_TRANSPARENT 收不到点击的透明

        SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_BORDER & ~WS_CAPTION); // 无边框、无标题栏



        // 设置窗体位置为右下角

        currentX = Screen.currentResolution.width - 900;

        currentY = Screen.currentResolution.height  - 800;

        SetWindowPos(hwnd, -1, currentX, currentY, 1200, 900, SWP_SHOWWINDOW); // Screen.currentResolution.width



        // 扩展窗口到客户端区域 -> 为了透明

        var margins = new MARGINS() { cxLeftWidth = -1 }; // 边距内嵌值确定在窗口四侧扩展框架的距离 -1为没有窗口边框

        DwmExtendFrameIntoClientArea(hwnd, ref margins);     

        // 将该窗口颜色为0的部分设置为透明,即背景可穿透点击,人物模型上不穿透

        SetLayeredWindowAttributes(hwnd, 0, 255, 1);

        /// <summary>

        ///设置窗体可穿透点击的透明.

        ///参数1:窗体句柄

        ///参数2:透明颜色  0为黑色,按照从000000到FFFFFF的颜色,转换为10进制的值

        ///参数3:透明度,设置成255就是全透明

        ///参数4:透明方式,1表示将该窗口颜色为0的部分设置为透明,2表示根据透明度设置窗体的透明度

        /// </summary>



#endif



        /// <summary>

        /// 1

        /// 调节窗体透明度可以先使用SetWindowLong为窗体加上WS_EX_LAYERED属性,

        /// 再使用SetLayeredWindowAttributes来指定窗体的透明度。

        /// 这样就可以在程序运行时动态的调节窗体的透明度了。

        /// 2

        /// 给 GWL_EXSTYLE 设置 WS_EX_TRANSPARENT 让窗口透明,此时应用程序只能收到鼠标消息但收不到触摸消息

        /// 3

        /// 前面加上取反操作符"~",就可以得到相反效果。比如,WS_CAPTION代表窗口有标题栏,~WS_CAPTION代表窗口没有标题栏

        /// 4

        /// GWL_STYLE指的是那些旧的窗口属性。相对于GWL_EXSTYLEGWL扩展属性而言的

        /// 5

        /// 要给窗口添加某属性,用 | 来连接,要去除某属性,用 & 来连接

        /// </summary>

    }

}


#####################################################################

相机参数:







交互
现在我们的桌宠只能看不能摸,也不会发出声音,下面我们来编写一些交互的代码。这里直接挂在宠物对象上。

#####################################################################
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class PetManager : MonoBehaviour
{
    public Camera game_camera;
    [SerializeField] public Collider coll;
    Vector3 offset;
    float depth;
    private float ySpeed = 120.0f; //旋转视角时相机y轴转速

    private int yMinLimit = -360;
    private int yMaxLimit = 360;

    private float x = 0.0f; //存储相机的euler角
    private float y = 0.0f; //存储相机的euler角

    private Vector3 mouse_position;

    public GameObject PetMenuUICanvas;

    public GameObject PetMenuUIMain;

    private RectTransform PetMenuUIMainRTF;

    private float TimeGo;

    void Start()
    {
        coll = GetComponent<Collider>();

        PetMenuUIMainRTF = PetMenuUIMain.GetComponent<RectTransform>();

        //宠物初始化时,UI菜单也要初始化位置,跟随宠物的位置
        //PetMenuUIMain.GetComponentInChildren<Button>().GetComponent<RectTransform>().anchoredPosition = new Vector2((this.transform.position.x - (float)5) * (float)27, (this.transform.position.y + (float)3) * (float)27);
        PetMenuUIMainRTF.anchoredPosition = new Vector2((this.transform.position.x) * (float)54, (this.transform.position.y) * (float)54);

    }
    void Update()
    {
        TimeGo = TimeGo + Time.deltaTime;

        //print(TimeGo);

        if (Input.GetMouseButton(0))
        {
            print("鼠标左键点击");
        }

        Ray ray = game_camera.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (coll.Raycast(ray, out hit, 10.0f))
        {
            if (Input.GetMouseButton(1))
            {
                print("鼠标右键点击");
                y -= Input.GetAxis("Mouse Y") * ySpeed * 0.2f;
                y = ClampAngle(y, yMinLimit, yMaxLimit);
                transform.rotation = Quaternion.Euler(0, y + 180, 0);

                //打开UI菜单需要1秒的间隔,不能频繁打开
                if (TimeGo>1)
                {
                    if (PetMenuUICanvas.activeSelf == false)
                    {
                        PetMenuUICanvas.SetActive(true);
                    }
                    else
                    {
                        PetMenuUICanvas.SetActive(false);
                    }

                    TimeGo = 0;
                }


            }
        }
    }

    //鼠标拖拽方法--宠物拖拽
    void OnMouseDrag()
    {
        print("拖拽开始");
        mouse_position = Camera.main.ScreenToWorldPoint(Input.mousePosition);

        transform.position = new Vector3(mouse_position.x, mouse_position.y, 0);

        //PetMenuUIMain.GetComponentInChildren<Button>().GetComponent<RectTransform>().anchoredPosition = new Vector2((mouse_position.x + (float)35.5) * (float)27, (mouse_position.y + (float)20) * (float)27);

        //拖拽宠物时,UI菜单跟随
        //PetMenuUIMain.GetComponentInChildren<Button>().GetComponent<RectTransform>().anchoredPosition = new Vector2((mouse_position.x - (float)5) * (float)27, (mouse_position.y + (float)3) * (float)27);
        //PetMenuUIMain.GetComponent<RectTransform>().anchoredPosition = new Vector2((mouse_position.x - (float)5) * (float)27, (mouse_position.y + (float)3) * (float)27);
        //PetMenuUIMain.GetComponent<RectTransform>().anchoredPosition = new Vector2((mouse_position.x) * (float)27, (mouse_position.y) * (float)27);

        //4K分辨率
        PetMenuUIMainRTF.anchoredPosition = new Vector2((mouse_position.x) * (float)54, (mouse_position.y) * (float)54);

        //print(PetMenuUIMain.GetComponentInChildren<Button>().GetComponent<RectTransform>().anchoredPosition.x+":"+ PetMenuUIMain.GetComponentInChildren<Button>().GetComponent<RectTransform>().anchoredPosition.y);

        //print(PetMenuUIMain.GetComponent<RectTransform>().anchoredPosition.x+":"+ PetMenuUIMain.GetComponent<RectTransform>().anchoredPosition.y);

    }


    static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}

##################################################################


此代码我加了个UI菜单跟随,Unity层级如下所示:






项目打包设置
因为此帖子使用的unity版本是2022.3.40f1c1版本,需要额外设置一个选项才能使得窗口透明化。



在打包界面的这里,取消勾选这个选项,然后在打包,就可以实现窗口化透明了。








本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册成为修仙之旅的少年~

x
游客
回复
*滑块验证:

DA论坛飞机票来了~
快速回复 返回顶部 返回列表