【unity小技巧】手戳代码程序化绘制地形Terrain树和预制体物品、动物

文章目录

  • 基础
  • 每次运行,随机批量绘制地形树
  • 指定某些地形纹理不生成树木
  • 可能你还想加入坡度限制
  • 将地形树替换成预制体树
  • 其实也可以直接在地形上绘制生成树预制体
  • 要在地形上程序化放置物品和动物
  • 完结

基础

【2023Unity游戏开发教程】零基础带你从小白到超神04——地形的绘制和基础使用介绍

每次运行,随机批量绘制地形树

public class TreeSpawner : MonoBehaviour
{public int numberOfTrees = 100; // 需要生成的树木数量public float spawnRadius = 50f; // 生成范围半径private Terrain terrainInstance;private TreePrototype[] treePrototypes; // 树木原型数组void Start(){terrainInstance = GetComponent<Terrain>();treePrototypes = terrainInstance.terrainData.treePrototypes; // 获取树木原型数组ClearTrees(); // 清空树木数据SpawnTrees();}void ClearTrees(){terrainInstance.terrainData.treeInstances = new TreeInstance[0];}void SpawnTrees(){for (int i = 0; i < numberOfTrees; i++){// 在指定范围内随机生成树木位置Vector3 randomPosition = new Vector3(Random.Range(0, terrainInstance.terrainData.size.x), 0, Random.Range(0, terrainInstance.terrainData.size.z));randomPosition.y = terrainInstance.SampleHeight(randomPosition) + terrainInstance.transform.position.y;if (Vector3.Distance(randomPosition, transform.position) < spawnRadius){TreeInstance newTree = new TreeInstance();newTree.position = new Vector3(randomPosition.x / terrainInstance.terrainData.size.x, randomPosition.y / terrainInstance.terrainData.size.y, randomPosition.z / terrainInstance.terrainData.size.z);newTree.widthScale = 1f;newTree.heightScale = 1f;// newTree.prototypeIndex = 0; // 使用第一个树木原型newTree.prototypeIndex = Random.Range(0, treePrototypes.Length);terrainInstance.AddTreeInstance(newTree);}}terrainInstance.Flush(); // 刷新地形数据}
}

指定某些地形纹理不生成树木

using UnityEngine;public class TreeSpawner : MonoBehaviour
{public int numberOfTrees = 100; // 需要生成的树木数量public float spawnRadius = 50f; // 生成范围半径private Terrain terrainInstance;private TreePrototype[] treePrototypes; // 树木原型数组void Start(){terrainInstance = GetComponent<Terrain>();treePrototypes = terrainInstance.terrainData.treePrototypes; // 获取树木原型数组ClearTrees(); // 清空树木数据SpawnTrees();}void ClearTrees(){terrainInstance.terrainData.treeInstances = new TreeInstance[0];}void SpawnTrees(){// 获取地形纹理的混合权重数组float[,,] alphamap = terrainInstance.terrainData.GetAlphamaps(0, 0, terrainInstance.terrainData.alphamapWidth, terrainInstance.terrainData.alphamapHeight);for (int i = 0; i < numberOfTrees; i++){// 在指定范围内随机生成树木位置Vector3 randomPosition = new Vector3(Random.Range(0, terrainInstance.terrainData.size.x), 0, Random.Range(0, terrainInstance.terrainData.size.z));randomPosition.y = terrainInstance.SampleHeight(randomPosition) + terrainInstance.transform.position.y;if (Vector3.Distance(randomPosition, transform.position) < spawnRadius){// 获取树木位置所在的地形纹理索引int textureIndexX = (int)(randomPosition.x / terrainInstance.terrainData.size.x * terrainInstance.terrainData.alphamapWidth);int textureIndexY = (int)(randomPosition.z / terrainInstance.terrainData.size.z * terrainInstance.terrainData.alphamapHeight);// 获取树木位置的地形纹理混合权重float[] textureWeights = new float[terrainInstance.terrainData.alphamapLayers];for (int layer = 0; layer < terrainInstance.terrainData.alphamapLayers; layer++){//textureWeights保存的其实就是每个地形纹理的混合比例textureWeights[layer] = alphamap[textureIndexY, textureIndexX, layer];// Debug.Log(textureWeights[layer]);}// 判断是否满足条件不生成树木,// 如果第一个草地层的混合为小于0.5则不生成树,你可以简单的理解为只在第一个草地层绘制树if (textureWeights[0] < 0.5f){continue;  // 跳过不生成树木}TreeInstance newTree = new TreeInstance();newTree.position = new Vector3(randomPosition.x / terrainInstance.terrainData.size.x, randomPosition.y / terrainInstance.terrainData.size.y, randomPosition.z / terrainInstance.terrainData.size.z);newTree.widthScale = 1f;newTree.heightScale = 1f;// newTree.prototypeIndex = 0; // 使用第一个树木原型newTree.prototypeIndex = Random.Range(0, treePrototypes.Length);terrainInstance.AddTreeInstance(newTree);}}terrainInstance.Flush(); // 刷新地形数据}
}

可能你还想加入坡度限制

自己定义的坡度限制值。当地形坡度超过这个限制时,将跳过树木生成

using UnityEngine;public class TreeSpawner : MonoBehaviour
{public int numberOfTrees = 100; // 需要生成的树木数量public float spawnRadius = 50f; // 生成范围半径public float slopeLimit = 30f;//坡度限制private Terrain terrainInstance;private TreePrototype[] treePrototypes; // 树木原型数组void Start(){terrainInstance = GetComponent<Terrain>();treePrototypes = terrainInstance.terrainData.treePrototypes; // 获取树木原型数组ClearTrees(); // 清空树木数据SpawnTrees();}void ClearTrees(){terrainInstance.terrainData.treeInstances = new TreeInstance[0];}void SpawnTrees(){// 获取地形纹理的混合权重数组float[,,] alphamap = terrainInstance.terrainData.GetAlphamaps(0, 0, terrainInstance.terrainData.alphamapWidth, terrainInstance.terrainData.alphamapHeight);for (int i = 0; i < numberOfTrees; i++){// 在指定范围内随机生成树木位置Vector3 randomPosition = new Vector3(Random.Range(0, terrainInstance.terrainData.size.x), 0, Random.Range(0, terrainInstance.terrainData.size.z));randomPosition.y = terrainInstance.SampleHeight(randomPosition) + terrainInstance.transform.position.y;// 检查地形坡度float slopeAngle = terrainInstance.terrainData.GetSteepness(randomPosition.x / terrainInstance.terrainData.size.x, randomPosition.z / terrainInstance.terrainData.size.z);if (slopeAngle > slopeLimit){continue;  // 跳过不生成树木}if (Vector3.Distance(randomPosition, transform.position) < spawnRadius){// 获取树木位置所在的地形纹理索引int textureIndexX = (int)(randomPosition.x / terrainInstance.terrainData.size.x * terrainInstance.terrainData.alphamapWidth);int textureIndexY = (int)(randomPosition.z / terrainInstance.terrainData.size.z * terrainInstance.terrainData.alphamapHeight);// 获取树木位置的地形纹理混合权重 terrainInstance.terrainData.alphamapLayers获取当前地形中使用的地形纹理数量float[] textureWeights = new float[terrainInstance.terrainData.alphamapLayers];for (int layer = 0; layer < terrainInstance.terrainData.alphamapLayers; layer++){//textureWeights保存的其实就是每个地形纹理的混合比例textureWeights[layer] = alphamap[textureIndexY, textureIndexX, layer];// Debug.Log(textureWeights[layer]);}// 判断是否满足条件不生成树木,// 如果第一个草地层的混合为小于0.5则不生成树,你可以简单的理解为只在第一个草地层绘制树if (textureWeights[0] < 0.5f){continue;  // 跳过不生成树木}TreeInstance newTree = new TreeInstance();newTree.position = new Vector3(randomPosition.x / terrainInstance.terrainData.size.x, randomPosition.y / terrainInstance.terrainData.size.y, randomPosition.z / terrainInstance.terrainData.size.z);newTree.widthScale = 1f;newTree.heightScale = 1f;// newTree.prototypeIndex = 0; // 使用第一个树木原型newTree.prototypeIndex = Random.Range(0, treePrototypes.Length);terrainInstance.AddTreeInstance(newTree);}}terrainInstance.Flush(); // 刷新地形数据}
}

效果
在这里插入图片描述

将地形树替换成预制体树

为了性能考虑,实现只替换角色附近指定范围的树,这样后续还可以使用对象池优化代码

public class TreeReplace : MonoBehaviour
{public Transform player;// 设置替换范围中心点public float radius = 10;// 设置替换范围半径Terrain terrainInstance;private GameObject tree;void Start(){terrainInstance = GetComponent<Terrain>();TreeInstance[] trees = terrainInstance.terrainData.treeInstances; // 获取地形上的树木实例数组for (int i = 0; i < trees.Length; i++){Vector3 worldPosition = terrainInstance.transform.position + Vector3.Scale(trees[i].position, terrainInstance.terrainData.size);//转换为世界坐标if (Vector3.Distance(player.position, worldPosition) <= radius){TreePrototype treePrototype = terrainInstance.terrainData.treePrototypes[trees[i].prototypeIndex]; // 获取树木实例对应的原型tree = treePrototype.prefab;//树木预制体Instantiate(tree, worldPosition, Quaternion.identity);trees[i] = new TreeInstance(); // 将该位置的树木实例置为空}}terrainInstance.terrainData.treeInstances = trees;terrainInstance.terrainData.RefreshPrototypes();}
}

其实也可以直接在地形上绘制生成树预制体

public class TreeSpawner : MonoBehaviour
{public GameObject[] treePrefabs; // 多种树木预制体public int numberOfTrees = 100; // 需要生成的树木数量public float spawnRadius = 50f; // 生成范围半径private Terrain terrainInstance;void Start(){terrainInstance = GetComponent<Terrain>();SpawnTrees();}void SpawnTrees(){for (int i = 0; i < numberOfTrees; i++){// 在指定范围内随机生成树木位置Vector3 randomPosition = new Vector3(Random.Range(0, terrainInstance.terrainData.size.x), 0, Random.Range(0, terrainInstance.terrainData.size.z));randomPosition.y = terrainInstance.SampleHeight(randomPosition) + terrainInstance.transform.position.y;if (Vector3.Distance(randomPosition, transform.position) < spawnRadius){// 生成随机索引,用于选择一个随机的树木预制体int randomIndex = Random.Range(0, treePrefabs.Length);GameObject selectedTreePrefab = treePrefabs[randomIndex];// 实例化所选的树木预制体,并进行位置和缩放调整GameObject newTree = Instantiate(selectedTreePrefab, randomPosition, Quaternion.identity);newTree.transform.localScale = Vector3.one; // 根据需要进行缩放调整// 可以在此处对新生成的树木进行其他设置或操作terrainInstance.Flush(); // 刷新地形数据}}}
}

要在地形上程序化放置物品和动物

可以使用类似生成树木预制体的方法,不过要注意的是需要避免与具有碰撞体的物体冲突,通常我们还需要排除某些层的检测,比如地形层,石头层

using UnityEngine;public class ObjectSpawner : MonoBehaviour
{public GameObject[] objectsToSpawn; // 需要生成的物品和动物Prefab数组public int numberOfObjects = 50; // 需要生成的数量public float spawnRadius = 50f; // 生成范围半径private Terrain terrainInstance;private Collider terrainCollider;void Start(){terrainInstance = GetComponent<Terrain>();terrainCollider = terrainInstance.GetComponent<Collider>();SpawnObjects();}void SpawnObjects(){for (int i = 0; i < numberOfObjects; i++){Vector3 randomPosition = new Vector3(Random.Range(0, terrainInstance.terrainData.size.x), 0, Random.Range(0, terrainInstance.terrainData.size.z));randomPosition.y = terrainInstance.SampleHeight(randomPosition) + terrainInstance.transform.position.y;if (Vector3.Distance(randomPosition, transform.position) < spawnRadius){// 排除两个层// int layerMask = ~(LayerMask.GetMask("Layer1") | LayerMask.GetMask("Layer2"));// 射线检测,检查生成位置是否与其他物体的碰撞体相交bool isColliding = Physics.CheckSphere(randomPosition, 1f, ~LayerMask.GetMask("Terrain"));if (!isColliding){// 实例化需要生成的物品和动物PrefabGameObject objectToSpawn = objectsToSpawn[Random.Range(0, objectsToSpawn.Length)];Instantiate(objectToSpawn, randomPosition, Quaternion.identity);}}}}
}

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

github请求超时解决方法

github请求超时解决办法 我使用windows执行如下git命令,提示超时 git clone xxxxx命令行提示如下&#xff1a; Failed to connect to github.com port 443: Timed out问题排查 可我Chrome可以正常访问github甚至ChatGPT&#xff0c;但是为什么在命令行里面却无法访问&#…

AI大语言模型学习笔记之三:协同深度学习的黑魔法 - GPU与Transformer模型

Transformer模型的崛起标志着人类在自然语言处理&#xff08;NLP&#xff09;和其他序列建模任务中取得了显著的突破性进展&#xff0c;而这一成就离不开GPU&#xff08;图形处理单元&#xff09;在深度学习中的高效率协同计算和处理。 Transformer模型是由Vaswani等人在2017年…

Vue学习笔记之生命周期函数

生命周期示意图如下所示&#xff1a; beforeCreate&#xff1a;组件初始化之前触发该事件created&#xff1a;组件初始化完毕触发该事件beforeMount&#xff1a;Vue应用对象挂载DOM结点之前触发该事件mounted&#xff1a;DOM结点挂载成功之后触发该事件beforeUpdate&#xff1a…

怎么控制Element的数据树形表格展开所有行;递归操作,打造万能数据表格折叠。

HTML <el-button type"success" size"small" click"expandStatusFun"> <span v-show"expandStatusfalse"><i class"el-icon-folder-opened"></i>展开全部</span><span v-show"expan…

新建VM虚拟机-安装centOS7-连接finalshell调试

原文 这里有问题 首先进入/etc/sysconfig/network-scripts/目录 cd /etc/sysconfig/network-scripts/ 然后编辑文件 ifcfg-ens33 vi ifcfg-ens33

知识点积累系列(一)golang语言篇

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 知识点积累 系列文章的第一篇&#xff0c;记录golang语言相关的知识点 1.结构体的mapstructure是什么 mapstructure:"default" mapstructure是一个Go语言的库&#xff0c;用于将一个map中的值映射到…

少儿编程 中国电子学会图形化编程2021年3月等级考试Scratch三级真题解析(选择题、判断题)

1.在《采矿》游戏中&#xff0c;当角色捡到黄金时财富值加1分&#xff0c;捡到钻石时财富值加2分&#xff0c;下面哪个程序实现这个功能&#xff1f; A&#xff1a; B&#xff1a; C&#xff1a; D&#xff1a; 2.设计一个和在20以内&#xff08;包括20&#xff09;的整数加法…

2024年【T电梯修理】及T电梯修理复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 T电梯修理是安全生产模拟考试一点通总题库中生成的一套T电梯修理复审模拟考试&#xff0c;安全生产模拟考试一点通上T电梯修理作业手机同步练习。2024年【T电梯修理】及T电梯修理复审模拟考试 1、【多选题】工作结束跨…

部署PXE高效批量网络装机

部署PXE高效批量网络装机 因在Cisco3850核心交换机中已开启DHCP 服务&#xff0c;因此不需要在配置DHCP服务。如果您的网络环境中也已有DHCP服务&#xff0c;也不用再配置DHCP服务了&#xff0c;直接部署PXE相关服务即可。 找一台linux系统的服务器&#xff0c;这本次试验用的是…

32定时器定时输出比较输入捕获编码器接口

一.定时器简介 1.基本定时器 2.通用定时器 滤波器可以滤掉信号的抖动和干扰&#xff0c;其工作原理&#xff1a;在一个固定的时钟频率f下进行采样&#xff0c;如果连续n隔采样点都为相同的电平&#xff0c;那就代表输入的信号稳定了。如果采样值不全都相同&#xff0c;那就说明…

DataTable.Load(reader)注意事项

对于在C#中操作数据库查询&#xff0c;这样的代码很常见&#xff1a; using var cmd ExecuteCommand(sql); using var reader cmd.ExecuteReader(); DataTable dt new DataTable(); dt.Load(reader); ...一般的查询是没问题的&#xff0c;但是如果涉及主键列的查询&#xf…

(2024,CompAgent,LLM,提示分解,基于布局的对象组合)分而治之:语言模型可以规划和自我纠正组合文本到图像的生成

Divide and Conquer: Language Models can Plan and Self-Correct for Compositional Text-to-Image Generation 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 3. 方法 3.1…