unity2017官方已经支持了瓷砖,但我自己也写了个简单的自动瓷砖,权当学习,效果如图。
主要实现了两个功能:
① 根据周围格子的状态改变自己的状态
② 对2D物理碰撞体进行自动合并,优化性能
基本思路是遍历特定范围中的每个GameObjec,然后计算坐标,再通过坐标来处理瓷砖间的相对关系。先看基类,主要处理变换坐标的工作:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AutoTile : MonoBehaviour
{
/* 图块边长 */
public float sideLength;
/* 是否合并碰撞体 */
public bool isCoillder;
/* 左下角的起始块,标定开始计算的位置 */
public GameObject startOne;
/* 存储地图 */
List<ArrayList> mapContainer = new List<ArrayList>();
protected Transform[] childrenTransforms;
/* 存储子物体 */
protected Vector2 basePoint;
/* 从startone获取的起点 */
/* 初始化起点 */
protected void intinalBasePoint()
{
basePoint = startOne.transform.position;
/* Debug.Log ("BasePoint=" + basePoint); */
}
/* 初始化存储块信息的变长数组 */
protected void intinalMapContainer()
{
/* 获取子物体的数组 */
childrenTransforms = GetComponentsInChildren<Transform> ( false );
foreach ( Transform tra in childrenTransforms )
{
/* 利用tag判断是否纳入计算 */
if ( tra.tag == "Tile" )
{
Vector2 pos = tra.position;
int x, y;
x = (int) ( (pos.x - basePoint.x) / sideLength + 0.05f);
/* 加0.05防止误差 */
y = (int) ( (pos.y - basePoint.y) / sideLength + 0.05f);
/* 判断是否延长主组 */
while ( mapContainer.Count <= x )
{
mapContainer.Add( new ArrayList() );
}
/* 向列数组填充元素 */
while ( mapContainer [x].Count <= y )
{
mapContainer [x].Add( 0 );
}
mapContainer [x][y] = 1;
}
}
return;
}
/* 以下has***函数是用来判断的工具函数,在子类中用到 */
protected bool hasUp( int x, int y )
{
if ( x < 0 || y < 0 )
return(false);
if ( !(mapContainer.Count > x) || mapContainer [x] == null )
return(false);
if ( mapContainer [x].Count == y + 1 )
return(false); else
return( ( (int) mapContainer [x] [y + 1]) == 1);
}
protected bool hasDown( int x, int y )
{
if ( x < 0 || y < 1 )
return(false);
if ( !(mapContainer.Count > x) || mapContainer [x] == null )
return(false);
return( (int) mapContainer [x] [y - 1] == 1);
}
protected bool hasLeft( int x, int y )
{
if ( x < 1 || y < 0 )
return(false);
if ( !(mapContainer.Count > x) )
return(false);
if ( mapContainer [x - 1] == null || mapContainer [x - 1].Count < y + 1 )
return(false);
return( (int) mapContainer [x - 1] [y] == 1);
}
protected bool hasRight( int x, int y )
{
if ( x < 0 || y < 0 )
return(false);
if ( !(mapContainer.Count > x + 1) )
return(false);
if ( mapContainer [x + 1] == null || mapContainer [x + 1].Count < y + 1 )
return(false);
return( (int) mapContainer [x + 1] [y] == 1);
}
protected bool hasLeftUp( int x, int y )
{
return(hasLeft( x, y + 1 ) );
}
protected bool hasRightUp( int x, int y )
{
return(hasRight( x, y + 1 ) );
}
protected bool hasLeftDown( int x, int y )
{
return(hasLeft( x, y - 1 ) );
}
protected bool hasRightDown( int x, int y )
{
return(hasRight( x, y - 1 ) );
}
/* 测试用 */
protected void test()
{
foreach ( ArrayList arraylist in mapContainer )
{
string output = null;
foreach ( var e in arraylist )
{
output += e;
}
Debug.Log( output );
}
}
public virtual void updateAllTile()
{
}
public virtual void updateAroundTile( int x, int y )
{
}
}
子类,用来更改Sprite与合并碰撞体,为什么要写子类呢?因为我觉得有拐角与没有的可以分开处理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AutoEdgeTiles : AutoTile
{
/* 序列化不同的图片,也许有资源集之类更好的办法,还望大神能指点一二 */
public Sprite SpriteMid, SpriteLeft, SpriteRight, SpriteUp, SpriteDown,
SpriteUpLeft, SpriteUpRight, SpriteDownLeft, SpriteDownRight, SpriteUpDown, SpriteLeftRight,
SpriteOnlyOne, SpriteOnlyLeft, SpriteOnlyRight, SpriteOnlyUp, SpriteOnlyDown;
void Awake()
{
intinalBasePoint();
updateAllTile();
/*
* 测试用,testX,testY要声明
* test ();
* Debug.Log(hasLeftUp(testX, testY)+"t"+hasUp (testX, testY)+"t"+hasRightUp(testX, testY)+
* "n"+hasLeft(testX, testY)+"tt"+hasRight(testX, testY)+
* "n"+hasLeftDown(testX, testY)+"t"+hasDown(testX, testY)+"t"+hasRightDown(testX, testY));
*/
}
override public void updateAllTile()
{
/* 父类中 */
intinalMapContainer();
/* 遍历每个tile */
foreach ( Transform tra in childrenTransforms )
{
if ( tra.tag == "Tile" )
{
Vector2 pos = tra.position;
int x, y, code = 0;
x = (int) ( (pos.x - basePoint.x) / sideLength + 0.05f);
y = (int) ( (pos.y - basePoint.y) / sideLength + 0.05f);
if ( isCoillder )
changeCoillder2D( tra, x, y );
/*一种类似二进制的判别法
*顺序上左下右
*/
if ( hasUp( x, y ) )
code += 1;
if ( hasDown( x, y ) )
code += 4;
if ( hasLeft( x, y ) )
code += 8;
if ( hasRight( x, y ) )
code += 2;
tra.gameObject.GetComponent<SpriteRenderer> ().sprite = findSpriteWithCode( code, tra, x, y );
}
}
}
/* 在可破化地形情况下使用(没写) */
override public void updateAroundTile( int x, int y )
{
}
/* 改变角 */
void changeConer( Transform tra, int x, int y )
{
if ( !hasLeftUp( x, y ) && hasLeft( x, y ) && hasUp( x, y ) )
tra.Find( "conerLeftUp" ).gameObject.SetActive( true );
if ( !hasRightUp( x, y ) && hasRight( x, y ) && hasUp( x, y ) )
tra.Find( "conerRightUp" ).gameObject.SetActive( true );
if ( !hasRightDown( x, y ) && hasRight( x, y ) && hasDown( x, y ) )
tra.Find( "conerRightDown" ).gameObject.SetActive( true );
if ( !hasLeftDown( x, y ) && hasLeft( x, y ) && hasDown( x, y ) )
tra.Find( "conerLeftDown" ).gameObject.SetActive( true );
}
/* 假如是可碰撞体,就想办法合并Collider2D,节省性能并防止“抽搐”问题 */
void changeCoillder2D( Transform tra, int x, int y )
{
if ( hasUp( x, y ) && hasDown( x, y ) && hasLeft( x, y ) && hasRight( x, y ) )
{
tra.GetComponent<BoxCollider2D> ().enabled = false;
return;
} else if ( hasLeft( x, y ) )
{
tra.GetComponent<BoxCollider2D> ().enabled = false;
return;
} else if ( !hasLeft( x, y ) )
{
BoxCollider2D theBox = tra.GetComponent<BoxCollider2D> ();
int count = 1;
while ( hasRight( x + count - 1, y ) )
{
count++;
}
theBox.size = new Vector2( sideLength * count, sideLength );
theBox.offset = new Vector2( sideLength / 2.0f * (count - 1), 0 );
}
return;
}
Sprite findSpriteWithCode( int code, Transform tra, int x, int y )
{
switch ( code )
{
case 0:
return(SpriteOnlyOne);
case 1:
return(SpriteOnlyUp);
case 2:
return(SpriteOnlyRight);
case 3:
changeConer( tra, x, y );
return(SpriteDownLeft);
case 4:
return(SpriteOnlyDown);
case 5:
return(SpriteUpDown);
case 6:
changeConer( tra, x, y );
return(SpriteUpLeft);
case 7:
changeConer( tra, x, y );
return(SpriteLeft);
case 8:
return(SpriteOnlyLeft);
case 9:
changeConer( tra, x, y );
return(SpriteDownRight);
case 10:
return(SpriteLeftRight);
case 11:
changeConer( tra, x, y );
return(SpriteDown);
case 12:
changeConer( tra, x, y );
return(SpriteUpRight);
case 13:
changeConer( tra, x, y );
return(SpriteRight);
case 14:
changeConer( tra, x, y );
return(SpriteUp);
case 15:
changeConer( tra, x, y );
return(SpriteMid);
default:
changeConer( tra, x, y );
return(SpriteMid);
}
}
}