您的位置:首页 > 其它

SRPG游戏开发(六)第三章 绘制地图 - 三 创建自己的SrpgTile

2018-01-27 17:58 591 查看
返回目录

第三章 绘制地图

一        导入素材

http://blog.csdn.net/darkrabbit/article/details/79168225




二        绘制一张简单地图

http://blog.csdn.net/darkrabbit/article/details/79177935




三        创建自己的SrpgTile

这一节中,我们选择继承Rule Tile的方式来创建自己的SrpgTile。

1     创建SrpgTile.cs

经过我们之前的分析,我们知道地形需要一个类型,还有数据。我这里只添加两个,一个地形类型,一个是回避率,同样你可以按你的需求添加所需数据。

地形类型枚举事例TerrainType.cs:

using System;

namespace DR.Book.SRPG_Dev.Maps
{
/// <summary>
/// 地形类型
/// </summary>
[Serializable]
public enum TerrainType : byte
{
/// <summary>
/// 平地
/// </summary>
Plain,

/// <summary>
/// 湿地(沼泽)
/// </summary>
[Obsolete("Game Not Used", true)]
Swamp,

/// <summary>
/// 道路
/// </summary>
Road,
}
}


FE4中共26种,使用18种;这里创建的共3种,使用2种。其中可以看到湿地(Swamp)项目有个Obsolete
Attribute,这是一个小技巧,意思是这个地形虽然定义了,但不使用它,在编辑器的Inspector面板中也不会显示。如果以后的版本你需要使用,只要去除Obsolete就可以了。

创建好地形后,就该创建Tile文件了。

完成后的文件SrpgTile.cs:

using System;
using UnityEngine;

namespace DR.Book.SRPG_Dev.Maps
{
[Serializable]
[CreateAssetMenu(fileName = "New SRPG Tile.asset", menuName = "SRPG/Tile")]
public class SrpgTile : RuleTile
{
private TerrainType m_TerrainType = TerrainType.Plain;
private int m_AvoidRate = 0;

/// <summary>
/// 地形类型
/// </summary>
public TerrainType terrainType
{
get { return m_TerrainType; }
set { m_TerrainType = value; }
}

/// <summary>
/// 回避率
/// </summary>
public int avoidRate
{
get { return m_AvoidRate; }
set { m_AvoidRate = value; }
}
}
}


不要忘记添加“CreateAssetMenu”,否则我们在Create菜单中看不到它。

这样我们就我们完成了SrpgTile.cs的编写,如果你要创建它,可以选择菜单Assets/Create/SRPG/Tile。

2     创建SrpgTileEditor.cs

创建完SrpgTile后,发现Rule Tile的Inspector面板被我们破坏了。这就需要我们也继承它的Editor文件,而且还要在Inspector面板中添加我们的数据。

请注意原始的Rule Tile Editor类使用了internal class,我们需要将它改成public class。在Tile/Script下创建一个新的文件夹,重命名为Editor。在这个文件夹下创建SrpgTileEditor.cs。

完成后的文件SrpgTileEditor.cs:

using UnityEditor;

namespace DR.Book.SRPG_Dev.Maps
{
[CustomEditor(typeof(SrpgTile))]
[CanEditMultipleObjects]
public class SrpgTileEditor : RuleTileEditor
{
public SrpgTile srpgTile { get { return target as SrpgTile; } }

public override void OnInspectorGUI()
{
// 渲染新增的数据
EditorGUI.BeginChangeCheck();
srpgTile.terrainType = (TerrainType)EditorGUILayout.EnumPopup("Terrain Type", srpgTile.terrainType);
srpgTile.avoidRate = EditorGUILayout.IntSlider("Avoid Rate", srpgTile.avoidRate, -100, 100);
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(target);
}

// 渲染RuleTile的内容
EditorGUILayout.Space();
base.OnInspectorGUI();
}
}
}


这样,Inspector面板就显示了我们要的数据了。

3     测试SrpgTile

首先,在Tile/Asset中创建2个SrpgTile,并设置他们。



图 3 - 13设置SrpgTile

Default Sprite:Tiling Rules中没有合适的贴图时使用;
Default Collider:Collider类型,项目不需要;
Tiling Rules:Tile规则。
Rule:设置贴图旋转或镜像;
Collider:Collider类型,项目不需要;
Output:Tile类型,有普通(Single),随机(Random),动画(Animation)三个选项。

Tiling Rules中右侧有绿色箭头和红色叉子的地方是设置规则的地方,根据是否有邻居瓦片(Neighbor
Tile),选择其中的贴图,绿色代表有邻居,红色代表没邻居,空白代表不关心。


设置好之后,我们将其拖入Tile Palette中。选择它开始绘制地图。但你会发现两种Tile之间并没有很好的衔接,如图。



图 3 - 14没有衔接的Tile

这可不是我们想要的,我们希望不同地形之间也能很好的衔接。而产生的原因是因为Rule Tile判断邻居的方法限定了只有相同Tile才是邻居。这需要我们在Rule
Tile中找到相应的位置,并加以修改,让它认为只要有Tile就是邻居。


4     修改RuleTile.cs与SrpgTileEditor.cs

我们已经知道原因了,我们添加一个bool变量来控制邻居的类型是否为其本身,这样可以有选择的使用我们的Tile。

4.1      修改RuleTile.cs

打开RuleTile.cs,添加变量与检测方法:

/// <summary>
/// true: 只要旁边有Tile就使用Rule;
/// false: 旁边只有是自己时才使用Rule。
/// </summary>
public bool m_CheckAnyTile = false;

/// <summary>
/// 我们添加的方法,判断是不是采用规则(Rule)
/// true: 使用
/// false: 不使用
/// </summary>
/// <param name="neighbor"></param>
/// <param name="neighborTile"></param>
/// <returns></returns>
private bool NeighborMatches(TilingRule.Neighbor neighbor, TileBase neighborTile)
{
switch (neighbor)
{
/// 不关心,直接采用规则
case TilingRule.Neighbor.DontCare:
return true;

/// 绿色箭头,
/// 如果检测所有Tile,返回neighbor是不是不为null,
/// 否则返回neighbor是不是自己
case TilingRule.Neighbor.This:
return m_CheckAnyTile ? neighborTile != null : neighborTile == this;

/// 红色叉子,
/// 如果检测所有Tile,返回neighbor是不是为null,
/// 否则返回neighbor是不是不为自己
case TilingRule.Neighbor.NotThis:
return m_CheckAnyTile ? neighborTile == null : neighborTile != this;

default:
return false;
}
}


有了方法后,我们要找到使用它的地方,在RuleTile中有两处。

修改前代码:

public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle)
{
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
Vector3Int offset = new Vector3Int(x, y, 0);
Vector3Int rotated = GetRotatedPos(offset, angle);
int index = GetIndexOfOffset(rotated);
TileBase tile = tilemap.GetTile(position + offset);
if (rule.m_Neighbors[index] == TilingRule.Neighbor.This && tile != this || rule.m_Neighbors[index] == TilingRule.Neighbor.NotThis && tile == this)
{
return false;
}
}
}

}
return true;
}
public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY)
{
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
Vector3Int offset = new Vector3Int(x, y, 0);
Vector3Int mirrored = GetMirroredPos(offset, mirrorX, mirrorY);
int index = GetIndexOfOffset(mirrored);
TileBase tile = tilemap.GetTile(position + offset);
if (rule.m_Neighbors[index] == TilingRule.Neighbor.This && tile != this || rule.m_Neighbors[index] == TilingRule.Neighbor.NotThis && tile == this)
{
return false;
}
}
}
}

return true;
}


修改后的代码:

public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle)
{
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
Vector3Int offset = new Vector3Int(x, y, 0);
Vector3Int rotated = GetRotatedPos(offset, angle);
int index = GetIndexOfOffset(rotated);
TileBase tile = tilemap.GetTile(position + offset);
// 修改的地方
if (!NeighborMatches(rule.m_Neighbors[index], tile))
{
return false;
}
}
}

}
return true;
}
public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY)
{
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
Vector3Int offset = new Vector3Int(x, y, 0);
Vector3Int mirrored = GetMirroredPos(offset, mirrorX, mirrorY);
int index = GetIndexOfOffset(mirrored);
TileBase tile = tilemap.GetTile(position + offset);
// 修改的地方
if (!NeighborMatches(rule.m_Neighbors[index], tile))
{
return false;
}
}
}
}

return true;
}


4.2      修改SrpgTileEditor.cs

Editor的修改相对简单,只要加一条bool变量的就可以了。

修改后的代码:

public override void OnInspectorGUI()
{
// 渲染新增的数据
EditorGUI.BeginChangeCheck();
srpgTile.terrainType = (TerrainType)EditorGUILayout.EnumPopup("Terrain Type", srpgTile.terrainType);
srpgTile.avoidRate = EditorGUILayout.IntSlider("Avoid Rate", srpgTile.avoidRate, -100, 100);
// 添加的bool变量
srpgTile.m_CheckAnyTile = EditorGUILayout.Toggle("Check Any Tile", srpgTile.m_CheckAnyTile);
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(target);
}

// 渲染RuleTile的内容
EditorGUILayout.Space();
base.OnInspectorGUI();
}


5     再次测试SrpgTile

首先在Inspector面板中把2个Plain的SrpgTile的Check
Any Tile打勾。



图 3 - 15 Check Any Tile

再次在Scene面板中绘制地图,是不是好多了。



图 3 - 16不再衔接不正常

6    Tile之间的细线

你可能会发现,有的Tile之间会有一条细线,这是Unity渲染模式造成的,并不是Tile的问题。无论什么贴图,在同一张贴图中的两个Sprite如果是紧贴着的,有一定几率会出现这个问题,有两种解决办法,一种就是在每个Sprite边缘加一个像素,还一种是加入图集(加入图集时,Unity会自动在边缘补像素,查看图集可以看到),说道图集时再来详细说。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: