Unity演示Leetcode开香槟过程

news2025/1/21 7:14:47

文章目录

  • Unity演示Leetcode开香槟过程
    • 示意图一:
    • 示意图二(速度变为上图的5倍)
    • 主要步骤与难点
    • C#脚本代码:
    • 香槟杯子液体页面变化以及杯子边缘的绘画Shader代码
    • 杯子边缘液体流出的效果的Shader代码:

Unity演示Leetcode开香槟过程

在做Leetcode的799. 香槟塔题目时,发现这个题目很有趣,于是就想自己动手在Unity下面实现其动态过程,使其过程一幕了然,首先来一个GIF看下效果:

示意图一:

在这里插入图片描述

示意图二(速度变为上图的5倍)

在这里插入图片描述

主要步骤与难点

在做这个的过程中,主要步骤有以下几点:

  1. 香槟杯子的动态图变化需要用Shader实现
  2. 由于设计到求体积(严谨一点,和实际相贴近),为了计算简单,所以把杯子当作球面,然后根据容积不变,求出给定一个时间t,我们需要求出杯子底部到液体液面的距离h,这里会涉及到一元三次方程的求根问题,然后我的解决方式是用二分的方式,求一个比较接近的近似解。
  3. 香槟塔的对应关系,也就是leetcode题目中,也就是当倾倒了非负整数杯香槟后,返回第 i 行 j 个玻璃杯所盛放的香槟占玻璃杯容积的比例,然后用官方题解的算法即可。

C#脚本代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Threading.Tasks;


public class GObject
{
    public GameObject glass;
    public GameObject leftFlowWater;
    public GameObject RightFlowWater;
    public float height;
    float radius;
    float total_time;
    float maxVolume;


    private float volume;
    public GObject(GameObject obj, float _height) { 
        glass = obj;
        leftFlowWater = obj.transform.Find("leftflowWater").gameObject;
        RightFlowWater = obj.transform.Find("rightflowWater").gameObject;
        leftFlowWater.SetActive(false);
        RightFlowWater.SetActive(false);


        Material mat_left = new Material(leftFlowWater.GetComponent<Image>().material);
        leftFlowWater.GetComponent<Image>().material = mat_left;
        Material mat_right = new Material(RightFlowWater.GetComponent<Image>().material);
        RightFlowWater.GetComponent<Image>().material = mat_right;

        height = _height; 
        radius = _height / 2.0f;
        maxVolume = 2 / 3.0f * Mathf.PI * radius * radius * radius;
        Volume = 0.0f;
        total_time = 0.0f;
        SetVolume(Volume);
    }

    public float Volume
        {
            get { return volume; }
            set { volume = value; }
        }

    public void SetParent(GameObject parent_obj)
    {
        glass.transform.SetParent(parent_obj.transform);
    }

    public void SetHeight(float h)
    {

        Material mat = glass.GetComponent<Image>().material; // 获取材质
        mat.SetFloat("_WaterHight", h); // 设置 Shader 中某 Color 变量的值
    }

    public void SetPosition(float posX, float PosY)
    {
        glass.GetComponent<RectTransform>().anchoredPosition = new Vector3(posX, PosY, 0f);
    }

    public void SetSize(float width, float height)
    {
        glass.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height);
    }

    public void SetVolume(float val)
    {
        volume = val;
        
        if(val > maxVolume)
        {
            leftFlowWater.SetActive(true);
            RightFlowWater.SetActive(true);

            leftFlowWater.GetComponent<Image>().material.SetFloat("_StartVal", total_time);
            RightFlowWater.GetComponent<Image>().material.SetFloat("_StartVal", total_time);
            Debug.Log($"total_time = {total_time}");
            SetHeight(1.0f);
        }
        else if(val < 0.01f)
        {
            SetHeight(0.0f);
        }
        else
        {
            var _h = GetHeightByVolumeAsync(val);
            SetHeight(_h);
        }
    }

    public float GetHeightByVolumeAsync(float volume)
    {
        int num = 20;
        float start = 0;
        float end = radius;
        float R = radius;
        float cur_h = 0;
        float mid = radius / 2;
        float res = Mathf.PI * mid * mid * (R - mid / 3.0f);
        while (Mathf.Abs(volume - res) > 100.0f && start < end )
        {
            num--;
            mid = (start + end) / 2.0f;
            if (volume < res)
            {
                end = mid;
            }
            else
            {
                start = mid;
            }
            cur_h = (start + end) / 2.0f;
            res = Mathf.PI * cur_h * cur_h * (R - cur_h / 3.0f);
        }

        return cur_h / radius;
    }


    public void Refresh(float dt)
    {
        if(Volume > maxVolume)
        {
            total_time += dt;
        }
        SetVolume(Volume);
    }

}



public class Champagne : MonoBehaviour
{
    // Start is called before the first frame update

    GameObject glass;
    Vector2 startPos;
    Dictionary<int, Dictionary<int,GObject>> glassDic;
    int rowNum;
    int width;
    int height;
    int extraHeight;
    float totalVolume;
    float maxUnitVolume;
    public float speed;
    float total_time;


    private void Awake()
    {
        startPos = new Vector2(0, -50);
        rowNum = 5;
        width = height = 100;
        extraHeight = 30;
        glassDic = new Dictionary<int, Dictionary<int, GObject>>();
        speed = 500000;
        totalVolume = 0.0f;
        float radius = height / 2;
        maxUnitVolume = 2 / 3.0f * Mathf.PI * radius * radius * radius;
        total_time = 0.0f;
    }

    void Start()
    {
        InitChampagneGlass();
    }


    void InitChampagneGlass()
    {
        for(int i =0; i<rowNum; ++i)
            for(int j=0; j<i+1; ++j)
            {
                glass = Resources.Load<GameObject>("prefab/Image1");
                glass = Instantiate<GameObject>(glass);
                Material mat = new Material(glass.GetComponent<Image>().material);
                glass.GetComponent<Image>().material = mat;

                float posY = startPos.y - (height / 2.0f + extraHeight) * i;
                float posX = startPos.x - i * (3 / 4.0f * width) + j * (3 / 2.0f * width);
                GObject gObject = new GObject(glass, height);
                gObject.SetParent(gameObject);
                gObject.SetPosition(posX, posY);
                gObject.SetSize(width, height);
                if (! glassDic.ContainsKey(i))
                {
                    var cur_dic = new Dictionary<int, GObject>() { { j, gObject } };
                    glassDic[i] = cur_dic;
                }
                else
                {
                    var cur_dic = glassDic[i];
                    cur_dic.Add(j, gObject);
                }

            }
    }

    // Update is called once per frame
    void Update()
    {
        float addVal = Time.deltaTime * speed;
        totalVolume += addVal;
        glassDic[0][0].Volume = totalVolume;
        glassDic[0][0].Refresh(Time.deltaTime);
        List<float> curList = new List<float>() { totalVolume };
        for (int i =1; i<=rowNum; ++i)
        {
            List<float> nextList = new List<float>();
            for(int k=0; k<i+1; ++k)
            {
                nextList.Add(0);
            }
            for(int j = 0; j < i; ++j)
            {
                float num = curList[j];
                float _addVal = Mathf.Max(0, num - maxUnitVolume) / 2.0f;
                if(glassDic.ContainsKey(i))
                {
                    nextList[j] += _addVal;
                    glassDic[i][j].Volume = nextList[j];
                    glassDic[i][j].Refresh(Time.deltaTime);
                    nextList[j + 1] += _addVal;
                    glassDic[i][j + 1].Volume = nextList[j + 1];
                    glassDic[i][j + 1].Refresh(Time.deltaTime);
                }
                
            }
            curList = nextList;
        }
    }
    
}

香槟杯子液体页面变化以及杯子边缘的绘画Shader代码

Shader "Unlit/s_galss"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _WaterColor("waterColor", COLOR) = (1,1,1,1)
        _EdgeColor("edgeColor", COLOR) = (0,0,0,1)
        _BackGroundColor("background_color", COLOR) = (0,0,0,1)
        _WaterHight("waterHight", Range(0, 1.0) ) = 0.5
        _GlassThickness("glassThickness", Range(0, 0.2) ) = 0.05
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" }
        LOD 100

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _WaterColor;
            float4 _EdgeColor;
            float4 _BackGroundColor;
            float _WaterHight;
            float _GlassThickness;

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

            float sdfCircle(float2 tex, float2 center, float radius)
            {
                return -length(float2(tex - center)) + radius;

            }

            float sdfWater(float2 tex, float2 center, float radius, float h)
            {
                float dis0 = length(float2(tex - center));
                float dis1 = center.y - h;
                float2 p1 = tex - center;
                float dis2 = dot(p1, float2(0,-1));
                float rate = step(dis0, radius);
                return step(dis1, dis2 ) * rate;

            }


            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                float radius = 0.5;
                float edge_width = _GlassThickness;
                float4 BgColor = _BackGroundColor;
                float1x2 center = (0.5, 0.5);
                fixed4 col = tex2D(_MainTex, i.uv);
                float2 uv = i.uv;
                float d = sdfCircle(uv, center, radius);
                float anti = fwidth(d);
	            col =  lerp(BgColor, _EdgeColor, smoothstep(-anti, anti, d ));
                col.a = lerp(0, col.a, smoothstep(-anti, anti, d ));
                float d1 = sdfCircle(uv, center, radius - edge_width);
	            float anti1 = fwidth(d1);
	            float edge_alpha = smoothstep(-anti1, anti1, d1);
	            col = lerp(col, BgColor, edge_alpha);
                //col.a = lerp(col.a, 0, edge_alpha);

                // water 颜色
                float d_water = sdfWater(uv, center, radius - edge_width,  _WaterHight * (radius - edge_width) + edge_width);

                col = lerp(col, _WaterColor, d_water);
                col = lerp(col, BgColor, 1.0 - step(uv.y, 0.5)); // 不显示半圆之上的部分
                float a = lerp(col.a, 0, 1.0 - step(uv.y, 0.5));
                col.a = a;
                return col;
            }
            ENDCG
        }
    }
}

杯子边缘液体流出的效果的Shader代码:

Shader "Unlit/s_flow_water"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _WaterColor("waterColor", COLOR) = (1,0,0,1)
        _StartVal ("startValue", Range(0, 1.0)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" }
        LOD 100

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _WaterColor;
            float _StartVal;

            float curVal = 0.0f;

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

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = _WaterColor;
                // apply fog
                float2 uv = i.uv;
                float a = lerp(0, 1, step( 1.0f, uv.y + _StartVal / 0.3f));
                col.a = a;
                return col;
            }
            ENDCG
        }
    }
}

今天有点晚了,后续介绍算法介绍…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

shell中的printf实践:美颜的九九乘法表

一 简介 Linux系统中除了echo命令可以输出信息&#xff0c;还可以使用printf命令实现相同的效果。功能描述&#xff1a;printf命令可以格式化输出数据。printf命令的语法格式如下。 printf [格式] 参数 常用的格式字符串及功能描述下表&#xff1a; 应用案例&#xff1a; 输出…

配置anaconda环境变量

conda环境变量的配置配置环境变量找到anaconda安装位置打开环境变量配置设置输入python命令跳入商店问题总结配置环境变量 找到anaconda安装位置 首先下载好conda环境&#xff0c;例如我下载在D盘 打开环境变量配置设置 可以按winS进入搜索框&#xff0c;输入环境变量&…

PyQt5学习笔记--摄像头实时视频展示、多线程处理、视频编解码

目录 1--前言 2--基于Qt Designer设计ui文件 3--视频的编解码操作 4--完整代码 5--结果展示 6--存在的问题 7--参考 1--前言 ① 创建两个线程&#xff0c;主线程为ui线程&#xff0c;子线程用于读取摄像头视频&#xff0c;将处理后的图像帧数据&#xff08;处理操作可以…

开放域类型发现:Open Relation and Event Type Discovery with Type Abstraction

1 什么是type discovery? “relation discovery” (Yao et al., 2011; Marcheggiani and Titov, 2016),“open relation extraction” (Wu et al., 2019; Hu et al., 2020) and “event type induction” (Huang and Ji, 2020; Shen et al., 2021). In this paper, we unify …

[项目](美多商城)(数据库课设/软件工程大作业/软件工程课设)项目的创建、配置、说明文档与源码

文章目录&#x1f498; 相关说明&#x1f498; 美多商城前台前端项目创建与配置&#x1f496; 项目的创建&#x1f496; 配置UI组件库&#x1f49d; 安装&#x1f49d; 全局引入与全局注册&#x1f49d; 局部引入与局部注册&#x1f49d; 定制主题&#x1f496; 配置axios&…

【并发】Java并发线程池底层原理详解与源码分析(上)

【并发】Java并发线程池底层原理详解与源码分析&#xff08;上&#xff09; 线程池与线程对比 使用线程的方式运行 使用线程池的方式运行 分析 单线程、多线程、线程池效率问题 详细代码 结果分析 单线程为什么是最快的&#xff1f; 单线程都这么快了&#xff0c;我…

第四周 牛背山云海——拍拍大场景,自拍延时片段

目录4.1 面对广阔云海拍张全景照片&#xff0c;再来一组延时片段4.2 认识镜头滤镜4.3 补充技术基础知识&#xff1a;白平衡、色温4.4 小博士课堂——延时摄影课件4.1 面对广阔云海拍张全景照片&#xff0c;再来一组延时片段 云海 雪山 雅安境内的牛背山拍摄云海 牛背山可以看到…

java刷题day 04

一. 单选题&#xff1a; 解析&#xff1a;队列&#xff1a;先到先服务解析&#xff1a;final不能修饰接口&#xff0c;成员变量可以用final修饰 解析&#xff1a; A&#xff1a; 算法是指解题方案的准确而完整的描述&#xff0c;算法不等于程序&#xff0c;也不等于计算方法 BC…

Linux 时间同步 ntpdchrony 内网

Linux 时间同步 ntpd&chrony 在任何服务器集群中&#xff0c;为了更好的协同工作&#xff0c;除了一些互信机制外&#xff0c;需要的就是时间同步功能&#xff0c;如果时间不同步&#xff0c;就好比让在中国的同事与美国的同事进行沟通&#xff0c;会造成各种奇奇怪怪的时…

excel笔记

1.快速填充 当需要提取出电子邮件里的名称&#xff0c;可以在第一行里手动输入Nancy后&#xff0c;直接按快捷键ctrl e进行快速填充&#xff0c;可以直接获得预期数据 如果不用快捷键&#xff0c;也可以点击指定位置的图标&#xff0c;实现这一功能 快速填充不仅可以将进行…

大数据中的R语言——中国大学MOOC课程笔记

第一章 R语言简介 1.1 简介 R的特性&#xff1a; • 免费的 • 一个全面的统计研究平台 • 拥有顶尖水准的制图功能 • 一个可进行交互式数据分析和探索的强大平台 • 轻松地从各种类型的数据源导入数据&#xff0c;包括文本文件、数据库管理系统、统计软件&#xff0c;乃…

rabbitMQ 消息顺序性、消息幂等性、消息不丢失、最终一致性、补偿机制、消息队列设计

一、消息顺序性 消息队列中的若干消息如果是对同一个数据进行操作&#xff0c;这些操作具有前后的关系&#xff0c;必须要按前后的顺序执行&#xff0c;否则就会造成数据异常。 举例&#xff1a;   比如通过mysql binlog进行两个数据库的数据同步&#xff0c;由于对数据库的…

内存和函数

程序的内存布局 Linux默认情况下将高地址的1GB空间分配给内核&#xff0c;用户进程使用剩下2GB或者3GB的内存空间。在用户空间里&#xff0c;也有很多地址区间有特殊的地位&#xff0c;一般来讲&#xff0c;应用程序使用的内存空间里有如下"默认"的区域 1、栈&#…

疫情可视化part2

前言 这是疫情可视化最开始的文章&#xff0c;有需要了解的可前往查看&#xff1a;https://blog.csdn.net/xi1213/article/details/126824752。 本来说有时间就把这个项目完结了的&#xff0c;结果后面一直有事拖着&#xff0c;直到现在十一月份了才搞完。老样子&#xff0c;先…

F1. 生活在树上(easy version)树,dfs

题目链接 F1. 生活在树上&#xff08;easy version&#xff09; 题目背景 本题是 B 组的最后一题&#xff0c;是 F2 题的简单版本&#xff0c;两道题目的解法略有不同。本题和 F2 题在题意上的区别在于本题给定树上的边权&#xff0c;而不是点权。 小智生活在「传智国」&am…

汽车 Automotive > SOME/IP VS DDS调研和未来方向

参考&#xff1a;JASPAR, General incorporated association&#xff1a;What is the conqueror in the SOA platform for the future in-vehicle networks? 目录 SOME/IP介绍参考 DDS介绍 SOME/IP VS DDS 研究方向 SOME/IP介绍参考 汽车Automotive &#xff1e; SOME/…

MAC安全(防MAC泛洪攻击)

一、MAC地址表项分类&#xff1a; 1.1 动态表项&#xff1a;通过对帧内的源MAC进行学习而来&#xff0c;有老化时间 1.2 静态表项&#xff1a;由管理员手工配置&#xff0c;不会老化 1.3 黑洞表项&#xff1a;丢弃特定源MAC或目的MAC,不会老化 静态和黑洞表项不会被动态表项…

类与对象(下篇)

类与对象&#xff08;下&#xff09;再谈构造函数回顾构造函数初始化列表explicit 关键字拷贝构造函数也具有初始化列表友元 friend友元函数输入输出流的重载友元类static 成员内部类再谈构造函数 回顾构造函数 在上一篇博客中提到了构造函数&#xff0c;构造函数其主要目的是…

类与对象(中篇)

类中六个默认成员函数构造函数基本概念构造函数特性析构函数基本概念析构函数特性拷贝构造函数基本概念拷贝构造函数特性赋值运算符重载概念引入运算符重载函数的特性部分运算符的重载函数判等赋值前置 、前置--后置、后置--const 成员函数取地址只要生成一个类 &#xff0c;那…

iOS_Custom Transition Animation 自定义转场动画

文章目录1、push-pop 动画协议2、present-dismiss 动画协议3、实现转场动画协议3.1 动画时长3.2 push or present animation (显示动画)3.3 动画结束3.4 pop or dismiss animation (消失动画)4、UIPresentationController4.1 设置presentVC的frame4.2 present 动画4.3 dismiss …