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

通过C#脚本更改材质球的参数

        // 设置贴图
        Texture mTexture = Resources.Load("myTexture", typeof(Texture )) as Texture;
        material.SetTexture("_MainTex", mTexture );
        // 设置整数
        material.SetInt("_Int", 1);
        // 设置浮点
        material.SetFloat("_Float", 0.1f);
        // 设置颜色 rgba
        material.SetColor("_Color", Color.white);
        // 设置向量 xyzw
        material.SetVector("_Vector", new Vector4());
        // shader的正常接口是没有bool类型的。但通过#pragma multi_compile __ UNITY_name可以实现
        // 设置shader的UNITY_name为true
        material.EnableKeyword("UNITY_name");
        // 设置shader的UNITY_name为true
        material.DisableKeyword("UNITY_name");
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/lengyoumo/article/details/105167565

一、协程的概念

什么是协程? - 知乎 (zhihu.com)

我们都知道多线程,当需要同时执行多项任务的时候,就会采用多线程并发执行。拿手机支付举例子,当收到付款信息的时候,需要查询数据库来判断余额是否充足,然后再进行付款。

假设最开始我们只有可怜的10个用户,收到10条付款消息之后,我们开启启动10个线程去查询数据库,由于用户量很少,结果马上就返回了。

然而当用户开始爆炸增长,这时候有10000人同时在线付款,你打算启动10000个线程来处理任务。等等,问题来了,因为每个线程至少会占用4M的内存空间,10000个线程会消耗39G的内存,而服务器的内存配置只有区区8G,这时候你有2种选择,一是选择增加服务器,二是选择提高代码效率。那么是否有方法能够提高效率呢?

我们知道操作系统在线程等待IO的时候,会阻塞当前线程,切换到其它线程,这样在当前线程等待IO的过程中,其它线程可以继续执行。当系统线程较少的时候没有什么问题,但是当线程数量非常多的时候,却产生了问题。一是系统线程会占用非常多的内存空间,二是过多的线程切换会占用大量的系统时间。

协程刚好可以解决上述2个问题。协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。

回到上面的问题,我们只需要启动100个线程,每个线程上运行100个协程,这样不仅减少了线程切换开销,而且还能够同时处理10000个读取数据库的任务,很好的解决了上述任务。


什么是协程?

协程,从字面意义上理解就是协助程序的意思,我们在主任务进行的同时,需要一些分支任务配合工作来达到最终的效果

稍微形象的解释一下,想象一下,在进行主任务的过程中我们需要一个对资源消耗极大的操作时候,如果在一帧中实现这样的操作,游戏就会变得十分卡顿,这个时候,我们就可以通过协程,在一定帧内完成该工作的处理,同时不影响主任务的进行

首先需要了解协程不是线程,协程依旧是在主线程中进行

然后要知道协程是通过迭代器来实现功能的,通过关键字IEnumerator来定义一个迭代方法,注意使用的是IEnumerator


二、协程的使用

void Start ( )

{

  //第一种,无参可以直接使用方法名“字符串”来启用协程

   StartCoroutine("wjy");

  //第二种,无参可以调用方法来启用协程

  StartCoroutine(wjy());

  //有参

  StartCoroutine(jwy(1));

  //第三种,有参

  StartCoroutine("yjw",1,2);

}

IEnumerator  wjy()

{
    yield return null;
}

IEnumerator  jwy(int b)

{
    yield return null;
}

IEnumerator yjw(int i,int r)

{
    yield return null;
}

IEnumerator需要和yield配合使用

  • yield return null; 暂停协程等待下一帧继续执行

  • yield return 0或其他数字; 暂停协程等待下一帧继续执行

  • yield return new WairForSeconds(时间); 等待规定时间后继续执行

  • yield break; 跳出协程对应方法,其后面的代码不会被执行

  • yield return new WaitForEndOfFrame():等到所有相机画面被渲染完毕后更新

yield 位于 Update() 和 LateUpdate() 之间


三、使用 yield return new WaitForSeconds() 来控制材质球的变化

通过脚本和材质Shader的配合,给角色添加一个被击的效果

当点击被击Hit按钮时,角色的材质有原来的颜色变为红色,等待数秒后变回原来的颜色

可以使用协程yield return new WaitForSeconds()来实现

首先先创建一个空物体,命名为Scripts,用来挂载脚本。

然后创建脚本并命名为material02,在脚本中创建一个变量,用来存放角色。

public GameObject Monster;

将用于变化材质的对象赋予该变量Monster。

随后在场景中创建一个按钮Button

在脚本中添加一个方法OnHit()(角色被击的方法),将之前挂载了脚本的空对象拖到按钮的On Click()中

然后在方法Function中选择该脚本中的相关方法

创建好被击的方法后

再创建协程,然后在被击的方法中调用该协程,完成角色被击的效果

public void OnHit()

{

        StopAllCoroutines();

        StartCououtine(WaitBeHit());

}

IEnumerator WaitBeHit()

{

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color",Color.red);

        // 挂起,等待0.2s后继续执行下面的语句
        yield return new  WaitForSeconds(0.2f);

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color",Color.balck);

}

四、利用协程 yield return null 将循环中的内容分帧执行,然后实现角色被毒的效果 

首先也是需要创建被毒的按钮,将脚本中的被毒的方法OnDie()拖动到该按钮上

然后在协程中利用循环和Lerp()函数、yield return null 实现角色从绿色到黑色的渐变

yield return null ;   //表示暂停协程等待到下一帧后继续执行

该协程中_Time初始值为0,在循环中_Time每增加一个deltaTime,使用_Time/time的值来采样Lerp()函数中的值(time为自设值),当遇到yield return null时挂起循环,然后等待下一帧继续执行循环中的下一个,直到不满足循环的条件后跳出循环

所以这样就会实现角色在time时间段内,颜色由绿色渐变为源颜色

 public void OnDU()
 {
     Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material01;

     Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.DisableKeyword("_ClipEnable");

     Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip", -0.1f);

     //在一个协程开始前,停止所有协程
     StopAllCoroutines();

     //怪物中毒的协程(在多帧下完成一个函数,常用于变化动态效果)
     StartCoroutine(WaitDu(2f));
 }  

     //被毒协程
    IEnumerator WaitDu(float time)
    {
        float _Time = 0;
        while(_Time<time)
        {
            _Time += Time.deltaTime;
            //yield return new WaitForEndOfFrame();
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.Lerp(Color.green, Color.black, _Time / time));
            //yield return new WaitForEndOfFrame();
            yield return null;
        }
        yield break;
    }

 五、利用循环和协程 yield return null 以及 SetFloat实现角色逐渐熔解的效果

public void OnDie()
{
    Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material01;

    StopAllCoroutines();

    StartCoroutine(WaitDie(2f));
}

    IEnumerator WaitDie(float time)
    {
        float _Time=0;

        while(_Time<time)
        {
            _Time += Time.deltaTime;

            
            //设置颜色参数
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.black);
            //SetFloat用于设置Float类型的参数 例如_Clip

            //使变体有效,引号中需要写宏的名称
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.EnableKeyword("_CLIPENABLE_ON");

            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip",_Time/time);

            //挂起等待这一帧执行完后在继续执行该循环
            yield return null;
        }
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.black);
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip", -0.1f);
        yield break;
    }
}

当shader中有开关效果时,需要使用EnableKeyWord来打开变体开关

Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMateral.EnableKeyword("_CLIPENABLE_ON")

// _CLIPENABLE_ON为变体宏的名称


六、切换材质球


七、Material和sharedMaterial的区别

Material:

sharedMaterial:


【太妃糖耶】视频加载中,速速查收惊喜!

脚本:

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class material02 : MonoBehaviour
{
    public GameObject Monster;
    // Start is called before the first frame update

    public Material material;

    public Material material01;
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    public void Onclick()
    {
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material01;
        //修改材质球的颜色属性
        //Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color",Color.red);

        //在一个协程开始前,停止所有协程
        StopAllCoroutines();

        //怪物被击协程(等待)
        StartCoroutine(WaitBehit());
    }

    public void OnDU()
    {
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material01;

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.DisableKeyword("_ClipEnable");

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip", -0.1f);

        //在一个协程开始前,停止所有协程
        StopAllCoroutines();

        //怪物中毒的协程(在多帧下完成一个函数,常用于变化动态效果)
        StartCoroutine(WaitDu(2f));
    }

    public void OnDie()
    {
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material01;

        StopAllCoroutines();

        StartCoroutine(WaitDie(2f));
    }

    public void SwitchMaterial()
    {
        StopAllCoroutines();

            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial = material;
            
    }

    //定义关于被击协程
    IEnumerator WaitBehit()
    {
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.EnableKeyword("_ClipEnable");

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip", -0.1f);

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.red);

        yield return new WaitForSeconds(0.2f);

        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.black);
    }

    //被毒协程
    IEnumerator WaitDu(float time)
    {
        float _Time = 0;
        while(_Time<time)
        {
            _Time += Time.deltaTime;
            //yield return new WaitForEndOfFrame();
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.Lerp(Color.green, Color.black, _Time / time));
            //yield return new WaitForEndOfFrame();
            yield return null;
        }
        yield break;
    }

    IEnumerator WaitDie(float time)
    {
        float _Time=0;

        while(_Time<time)
        {
            _Time += Time.deltaTime;

            
            //设置颜色参数
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.black);
            //SetFloat用于设置Float类型的参数 例如_Clip

            //使变体有效,引号中需要写宏的名称
            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.EnableKeyword("_CLIPENABLE_ON");

            Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip",_Time/time);

            //挂起等待这一帧执行完后在继续执行该循环
            yield return null;
        }
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetColor("_Color", Color.black);
        Monster.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial.SetFloat("_Clip", -0.1f);
        yield break;
    }
}

角色Shader:

Shader"unity/practice04"
{
	Properties
	{
		_Color("Color",Color)=(0,0,0,0)
		_MainTex("MainTex",2D)="white"{}
		_DissolveTex("DissolveTex",2D)="white"{}
		_RampTex("RampTex",2D)="black"{}

		[Toggle]_ClipEnable("ClipEnable",int)=0
		_Clip("Clip",Range(-0.1,1))=0
	 }

	SubShader
	{
		Offset 1,1
		// UsePass"unity/X_Ray02/XRayPass"
		Pass
		{
			CGPROGRAM

			#pragma vertex vert;
			#pragma fragment frag;

			//在使用multi_compile变体时,前面须添加一个空变体才有效
			#pragma multi_compile _ _CLIPENABLE_ON

			#include "UnityCG.cginc"

			//声明变量
			sampler2D _MainTex;
			sampler2D _DissolveTex;
			float4 _DissolveTex_ST;
			sampler   _RampTex;         //优化  RampTex贴图只用到了一个方向的数值 
			fixed _Clip;
			float4 _Color;

			struct appdate
			{
				float4 vertex:POSITION;
				float4 uv:TEXCOORD;
			 };

			struct v2f
			{
				float4 pos:SV_POSITION;
				float4 uv:TEXCOORD;
			 };

			v2f vert(appdate v)
			{
				v2f o;
				o.pos=UnityObjectToClipPos(v.vertex);    //转换为齐次裁剪空间坐标

				//使第二张纹理贴图uv与主贴图的uv分开,方便调节DissolveTex贴图的平铺与偏移而不影响主贴图

				o.uv.xy=v.uv.xy;

				//o.uv.zw=v.uv*_DissolveTex_ST.xy+_DissolveTex_ST.zw;    
				o.uv.zw=TRANSFORM_TEX(v.uv,_DissolveTex);

				return o;
		     }

			float4 frag(v2f i):SV_TARGET
			{

				float4 c;
				float4 mainTex=tex2D(_MainTex,i.uv.xy);
				c=mainTex;
				c+=_Color;
				float4 dissolveTex=tex2D(_DissolveTex,i.uv.zw);
				

				//smoothstep 需要优化
				//float4 rampTex=tex1D(_RampTex,smoothstep(_Clip,_Clip+0.1,dissolveTex.r)); 

				#if _CLIPENABLE_ON
				fixed ramp=saturate((dissolveTex.r-_Clip)/(_Clip+0.1-_Clip));
				clip(dissolveTex.r-_Clip);
				float4 rampTex =tex2D(_RampTex,ramp);
				c+=rampTex;
				#endif

				return c;
			 }

			ENDCG
		 }
	 }
 }


http://www.kler.cn/a/592589.html

相关文章:

  • Flutter TextField 从入门到精通:掌握输入框的完整指南
  • 【链表】一文搞定链表算法:从基础到实战
  • 在线教育网站项目第四步:deepseek骗我, WSL2不能创建两个独立的Ubuntu,但我们能实现实例互访及外部访问
  • 记:app启动更换系统语言,app会重走生命周期
  • 【vue3+vant】移动端 - 部门树下拉选择组件 DeptTreeSelect 开发
  • rip 协议详细介绍
  • vue 中常用操作数组的方法
  • 【Python 的发展历史】
  • 【2025】基于Springboot + vue实现的毕业设计选题系统
  • 优选算法系列(2.滑动窗口_下)
  • C语言每日一练——day_12(最后一天)
  • 【江协科技STM32】软件I2C协议层读写MPU6050驱动层
  • 动态代理示例解析
  • 3.19学习总结
  • 递归分治法格雷码
  • 刷题练习笔记
  • 基于SpringBoot + Vue 的图书馆座位预约系统
  • 红日靶场(二)——个人笔记
  • HarmonyOS开发,console.log和hilog的区别,如何选择使用?
  • 两矩阵相乘(点乘和乘的区别)