全局重载运算符 Spring Cloud Pytorch uitableview cookies soap paypal datagrid jScroll vue框架 vue最新版本 后台管理模板下载 外卖系统源码 spark算法 mysql统计数量 mysql更新多个字段 quartz配置 手动安装fastboot驱动 python连接mysql python怎么入门 javatrim javamysql java使用 java写入文件 java的正则表达式 java获取月份 java运算 怎么安装java环境 java编程语言 java当前时间 java实现栈 java学习流程 java开发语言 蒙文字体 苹果剪辑 linux端口映射 skycc组合营销软件 千千静听老版本 lol语音包 明解c语言
当前位置: 首页 > 学习教程  > 编程语言

【Unity基础练习 构造分形(编程控制)】

2020/11/24 9:55:53 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

今天的教程来源于下方链接(它讲的更详细一点,我更多的只是总结) 构造分形(递归实现的细节) 今天这个练习,只需要自己创建一个空物体,一个材质,一个C#脚本即可运行。全部统一命名为F…

今天的教程来源于下方链接(它讲的更详细一点,我更多的只是总结)
构造分形(递归实现的细节)

今天这个练习,只需要自己创建一个空物体,一个材质,一个C#脚本即可运行。全部统一命名为Fractal。
在这里插入图片描述

以下是C#脚本,所有需要注意的地方我都用注释标识好了:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Fractal : MonoBehaviour
{
    public Mesh[] meshes;//随机使用mesh
    public Material material;
    public float childScale;//分形子类缩放大小
    public int maxDepth = 0;//设定递归最大深度,不然不断迭代至栈溢出

    public float spawnProbability;//分形创建概率,为了使分形不规则

    public float maxRotationSpeed;//旋转变量

    public float maxTwist;//附加一个扭曲使分形元素的排列更加随机
    private float rotationSpeed;//旋转变量
    private int depth = 0; //当前深度
    
    private static Vector3[] childDirections={//将方向变为静态数组以简洁化代码
        Vector3.up,
        Vector3.right,
        Vector3.left,
        Vector3.forward,
        Vector3.back
    };
    private static Quaternion[] childOrientations = {//将旋转变为静态数组以简化代码
        Quaternion.identity,
        Quaternion.Euler(0f,0f,-90f),
        Quaternion.Euler(0f,0f,90f),
        Quaternion.Euler(90f,0f,0f),
        Quaternion.Euler(-90f,0f,0f)
    };

    private Material[,] materials;//我们将显示制作动态批处理提升性能,使用二级颜色级数

    private void InitializeMaterials(){
        materials = new Material[maxDepth + 1,2];
        for(int i=0;i<=maxDepth;i++){//预先将材质全部制作好,这样就不必在分形复制的时候再创建
            float t = i/(maxDepth-1f);
            t *=t;
            materials[i,0] = new Material(material);
            materials[i,0].color = Color.Lerp(Color.white,Color.yellow,t);//让颜色线性变化
            materials[i,1] = new Material(material);
            materials[i,1].color = Color.Lerp(Color.white,Color.cyan,t);
        }
        materials[maxDepth,0].color = Color.magenta;//将最大深度的分形赋予洋红色
        materials[maxDepth,1].color = Color.red;
    }

    private void Start() {
        rotationSpeed = Random.Range(-maxRotationSpeed,maxRotationSpeed);
        transform.Rotate(Random.Range(-maxTwist,maxTwist),0f,0f);
        if(materials==null){
            InitializeMaterials();//如果没有材质则显式创建
        }
        gameObject.AddComponent<MeshFilter>().mesh= 
            meshes[Random.Range(0,meshes.Length)];//直接分配网格和材料给他们
        gameObject.AddComponent<MeshRenderer>().material = materials[depth,Random.Range(0,2)];//接↑ 目的是运行时可以自动添加
        if(depth<maxDepth){
            StartCoroutine(CreateChildren());//启动协程
        }
    }
    private void Update() {//让我们的分形动起来
        transform.Rotate(0f,rotationSpeed*Time.deltaTime,0f);
    }

    private void Initialize(Fractal parent,int childIndex){
        meshes = parent.meshes;//子分形附上父类的mesh,只传递mesh数组的引用
        materials = parent.materials;//附上父元素的materal,只传递材料数组的引用
        maxDepth = parent.maxDepth;//最大深度继承
        depth = parent.depth + 1;//每递归创建一次就令最大深度加1
        childScale = parent.childScale;//分形子类大小继承
        spawnProbability = parent.spawnProbability;//继承分形生成概率
        maxRotationSpeed = parent.maxRotationSpeed;//传递旋转速度
        maxTwist = parent.maxTwist;//传递扭曲量
        transform.parent = parent.transform;//设置父节点
        transform.localScale = Vector3.one*childScale;//设置分形太小
        transform.localPosition = childDirections[childIndex]*(0.5f+0.5f*childScale);//设置位置
        transform.localRotation = childOrientations[childIndex];//旋转是为了更好的展示视图
    }

    private IEnumerator CreateChildren(){//定义CreateChildren协程
        // yield return new WaitForSeconds(0.5f);
        // new GameObject("Fractal Child").
        //     AddComponent<Fractal>().Initialize(this,Vector3.up);//创建分形子类,包括指定方向
        //     //这里是脚本调用自身
        // yield return new WaitForSeconds(0.5f);
        // new GameObject("Fractal Child").
        //     AddComponent<Fractal>().Initialize(this,Vector3.right);//创建分形子类,包括指定方向
        //     //这里是脚本调用自身
        // yield return new WaitForSeconds(0.5f);
        // new GameObject("Fractal Child").
        //     AddComponent<Fractal>().Initialize(this,Vector3.left);//创建分形子类,包括指定方向
        //     //这里是脚本调用自身
        // 以上是简化之前的代码

        //以下是简化之后的代码
        for(int i=0;i<childDirections.Length;i++){
            if(Random.value<spawnProbability){//控制分形生成概率
            yield return new WaitForSeconds(Random.Range(0.1f,0.5f));//随机时间增长
            new GameObject("Fractal Child").AddComponent<Fractal>().
                Initialize(this,i);
            }
        }
    }
}

代码并不长,甚至可以说是代码本身是十分容易理解的。不过这次的练习让我学习到蛮多东西的。

1.首先是怎么考虑构造出最终分形的效果。是从复制物体->复制材质->添加随机数->添加随机mesh->添加扭曲。通过这样一个过程一步步构造,完成了最终的分形制作。

2.关于协程。上次在制作贪吃蛇的时候也使用到了协程,这里先大概介绍一下协程,之后我会单独写一篇博客来总结和说明其用法。

协同程序,是在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行。
这里的WaitForSeconds是Unity提供的时间延迟类。

//原本写法
  for(float timer = 0.0f; timer < 3.0f ; timer += Time.DeltaTime){
    yield return 0;//挂起,下一帧再来从这个位置继续执行。
  }
  //使用WaitForSeconds的写法
  yield return new WaitForSeconds(3.0f);

3.动态批处理
由于这个功能貌似已经在我当前Unity版本(2018+)淘汰了,所以这次的练习我们是显式去模仿动态批处理的方法的。关于动态批处理的作用,我个人理解是这样:
在这次的练习中,我们每一次递归创建分形的时候都会创建一次材质。而我们先显式的将所有材质创建好,组成一个静态数组,这样就可以省去每一次创建分形的材质创建,可以提升性能。
(动态批处理显式创建材质的代码已经在上方给出)

4.关于静态数组的使用
这次练习包括上次练习,还学到的一个十分重要的东西就是静态数组的使用。让静态数组存储我们程序中经常使用到的东西,然后使用循环索引去获取到这些存储起来的,资源/代码/属性。其实那天的函数雕刻,函数使用枚举存放,并且使用静态数组+委托调用的方式,也更像是使用静态数组的进阶用法。所以,如果要考虑怎么简化代码,让它更简洁,静态数组是个十分不错的方式。


本文链接: http://www.dtmao.cc/news_show_400179.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?