Unity2D:简单自动瓷砖(Tile)的实现

unity2017官方已经支持了瓷砖,但我自己也写了个简单的自动瓷砖,权当学习,效果如图。

image

主要实现了两个功能:

① 根据周围格子的状态改变自己的状态

② 对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);
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352