前面介绍的都是仅将贴图放置在unity场景中,而这些贴图并没有额外的属性,如果需要增加属性,则需要自己创建一个类,后面只需增加该类的属性就能保证所有地砖都能继承。
-
创建地砖类
我们创建一个新的脚本,但是该脚本继承的不是Unity默认的父类【MonoBehaviour】,而是【ScriptableObject】,这个类能直接通过Unity页面创建新的类来编辑
using System.Collections;
using UnityEngine;
[CreateAssetMenu(fileName = "新砖块", menuName = "砖块类")]
public class TileClass : ScriptableObject
//这个类是将所有砖块抽象出基本属性。
{
public string tileName; //砖块名字
public Sprite tileSprite; //砖块贴图
}
脚本保存后,就能在unity的资产内创建对应的新砖块类
创建了新的类后,能直接设置该类的名字和贴图元素,这样还有一个好处就是不需要额外修改切割sprite元素的名字。
补充:
如果想修改切割sprite的名字,需要进入到材质中编辑
选择每一个切割的元素重命名(右下角的name)
-
创建砖块集的类
由于整个游戏中有很多不同的砖块,如果不想通过脚本来引用类,可以创建一个类来通过unity的GUI来维护。每次新增新的砖块类型能直接在这里添加就可以。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "砖块集", menuName = "砖块集")]
public class TileAtlas : ScriptableObject
//这个类是用来绑定砖块类所对应的不同种类砖块所使用的素材
{
public TileClass stone; //岩石素材
public List<TileClass> dirtSprites = new List<TileClass>(); //复数的地块素材
public List<TileClass> grassSprites = new List<TileClass>(); //复数的草地素材
public List<TileClass> woodSprites = new List<TileClass>(); //复数的木头素材
public TileClass leftLeaf; //左侧树叶素材
public TileClass rightLeaf; //右侧树叶素材
}
同样右键创建该类,然后通过拖动
-
重构生成砖块的代码
由于生成了地砖集这个类,可以在沙盒地图生成的代码中将原本引用改成单独引用地图集类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sandbox_seed : MonoBehaviour
{
[Header("砖块材质")] //脚本参数中的标题
public TileAtlas tileAtlas; //创建该类就能直接调用子类中的各种砖块
}
接下来重构生成地砖的函数,将原本的 sprite 改成 tileClass ,原本的 stone 等 public元素改成类似tileAtlas.stone,部分子类中不是单纯的一个类,而是复数的类列表,所以也需要重构GetSprite函数成GetTileClass函数
public void GenerateTexture()
{
/* 前面的不动 */
TileClass tileClass; //创建局部变量用来进行判断该地砖应该是什么类型
//判断该生成的是什么地貌
if (y < surfaceNoise - dirtNoise) tileClass = tileAtlas.stone;
else if (y <= surfaceNoise - 1) tileClass = GetTileClass(tileAtlas.dirts);
else tileClass = GetTileClass(tileAtlas.grasses);
if (noiseTexture.GetPixel(x, y).r > caveFreq)
{
PlaceTile(tileClass, x, y);
}
}
}
}
GetSprite重构成GetTileClass函数,原理和原本的函数一样,改变的是返回的是tileClass类
public TileClass GetTileClass(List<TileClass> TileSet)
{
int randomIndex = Random.Range(0, TileSet.Count);
TileClass tileClass = TileSet[randomIndex];
return tileClass;
}
创建一个类List<Vector2> worldTileVectorList ,用于记录整个世界生成的砖块所在的(x,y)坐标。只有该位置没有砖块才能生成砖块(而如果类似泰拉瑞亚那种还有墙的话,需要增加墙面的列表来记录墙面)
public List<Vector2> worldTileVectorList = new List<Vector2>();
public void PlaceTile(TileClass tileSprite, int x, int y)
{
if (!worldTileVectorList.Contains(new Vector2(x, y))) //判断该地址是否已经存在了砖块,如果存在则不生成
{
GameObject newTile = new GameObject();
newTile.transform.parent = this.transform;
newTile.AddComponent<SpriteRenderer>();
newTile.GetComponent<SpriteRenderer>().sprite = tileSprite.tileSprite; //生成的元素为上面条件判断后的砖块
newTile.name = tileSprite.tileName;
newTile.transform.position = new Vector2(x, y);
worldTileVectorList.Add(newTile.transform.position);//将该地址记录下来
}
}
根据这个新增的判断我们可以增加地表生成树的代码
-
草地地砖上生成树
public float treeChance; //生成树的概率,1为每格都生成,0为不生成
public void GenerateTexture()
{
/* 前面代码不变 */
if (noiseTexture.GetPixel(x, y).r > caveFreq)
//GetPixel(x, y).r 是指该像素位置的RGB值中的R值,使用b和g都可以
//如果该像素所在位置的颜色比洞窟参数的颜色深(白)的话,就生成该砖块,否则留空为洞穴
{
PlaceTile(tileClass, x, y);
}
if (tileClass.tileName.Contains("Grass") && worldTileVectorList.Contains(new Vector2(x, y))) //如果这个砖块是草地砖块且生成成功,则进行判断是否生成树
{
float treeRandom = Random.Range(0f, 1f);
if (treeChance >= treeRandom) //当参数大于随机值,则生成树
{
PlaceTree(x, y);
}
}
}
}
}
生成树的代码如下,如果想要树的形状更多种,则添加各种随机判断生成树叶和木头的数量
public void PlaceTree(int x, int y)
{
int woodSpritesNum = Random.Range(0, 2); //树的材质,2选1
int treeHeight = Random.Range(6, 12); //树的随机高度
for (int i = 0; i < treeHeight; i++)
{
PlaceTile(tileAtlas.woods[woodSpritesNum], x, y + i + 1); //生成树砖块的坐标,为草地往上一格即y+1
if (i >= 4) //当大于4时开始生成树叶
{
for (int i2 = 1; i2 <= 2; i2++)
{
PlaceTile(tileAtlas.leftLeaf, x - i2, y + i + 1); //生成树砖块的坐标,为树往左右1格和2格
PlaceTile(tileAtlas.rightLeaf, x + i2, y + i + 1);
}
}
}
}
最终效果如下,可以看到在生成树的概率为1时,没有生成草地砖块的位置不生成树
如果想树生成得比较自然的,概率为0.2就可以了