ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2020-11-24 09:57:15  阅读:364  来源: 互联网

标签:parent 编程 分形 private Unity materials 0f new


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

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

标签:parent,编程,分形,private,Unity,materials,0f,new
来源: https://blog.csdn.net/weixin_43890220/article/details/110038854

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有