zoukankan      html  css  js  c++  java
  • Cocos2dx 学习笔记(3) 瓦片地图加载与触摸控制

    前言

      昨天完成了自定义类和脚本导出,今天假设场景结构,并加载TMX地图,实现地图拖动和点选的功能.

    预备工作

      1. 用Tiled地图编辑器做一张简单的地图,如下图:

      2. 将地图文件和地块纹理加入到资源目录.

    场景与层

      CCTMXTiledMap由CCTMXLayer组成,而CCTMXLayer是批量渲染节点,所以没办法所复杂纹理的运用.

      CCTMXTiledMap在类的头文件已经说明了,每一层Layer只能够有一张瓦片纹理,这就限制了虽然CCTMXTiledMap有很多层,但是无法作为复杂对象所在的层,还是专门做地图和区域标记的好.

      在我的设计中,主场景设定了多层Layer,CCTMXTiledMap看做一层,视为地面层(包括地下水层),而地上对象,天空等后续增加,与CCTMXTiledMap并列,没有包含关系.

      CCTMXTiledMap是一个灵活的地图容器,没有因为朝向的不同而被设计成不同的实现类,所以我在设计场景控制器的时候是选择分开的,根据朝向来创建不同的控制器,因为控制器到后来会内容会非常庞杂.

      具体设计如下:

        CMainScene: 主场景,支持多层,脚本导出

        IsometricController: 等轴模式场景控制器,用来控制地图拖拽,点击,拣选等操作,主要的逻辑触发点.

    代码解析

      一. 主场景类

    // -------------------------------------------------------
    // 文件名: MainScene.h
    // 描  述: 主场景
    // 日  期: 11/3/2013 
    // 作  者: KevinYuen
    // 版  权: Copyright (C) 2011 - All Rights Reserved
    // 备  注: 
    // -------------------------------------------------------
    
    #ifndef _MAINSCENE_H_
    #define _MAINSCENE_H_
    
    #include "cocos2d.h"
    
    USING_NS_CC;
    
    // 场景层
    typedef enum 
    {
    	ESLT_GROUND,	// 地面
    	ESLT_OBJECT1,	// 对象层
    	ESLT_OBJECT2,	// 对象层
    	ESLT_OBJECT3,	// 对象层
    	ESLT_SKY,		// 天空层
    	ESLT_GUI,		// 界面层
    	ESLT_CONTROL,	// 控制层
    	ESLT_FORCE32 = 0x7FFFFFFF
    } EnSceneLayerTag;
    
    class CMainScene : public CCScene
    {
    public:
    
    	// 析构函数
    	~CMainScene();
    
    public:
    
    	// 加载场景
    	bool loadScene( const char* tmx_file );
    
    	// 卸载场景
    	bool unloadScene();
    	
    protected:
    
    	// 加载地图
    	bool loadMap( const char* map_file );
    
    public:
    
    	// 静态create方法实现
    	CREATE_FUNC( CMainScene );
    };
    
    #endif
    
    // CPP
    
    #include "MainScene.h"
    #include "IsometricController.h"
    
    // 析构函数
    CMainScene::~CMainScene()
    {
    
    }
    
    // 加载场景
    bool CMainScene::loadScene( const char* tmx_file )
    {
    	// 进行加载
    	CCLOG( "[CMainScene] Prepare load scene from: %s.", tmx_file );
    
    	// 首先卸载当前场景
    	if( !unloadScene() )
    	{
    		CCLOG( "[CMainScene] unload old scene failed." );
    		return false;
    	}
    
    	// 加载地图
    	if( !loadMap( tmx_file ) )
    	{
    		CCLOG( "[CMainScene] create map filed. map file: %s.", tmx_file );
    	}
    
    	// 添加控制层
    	CCTMXTiledMap* pMap = dynamic_cast<CCTMXTiledMap*>( getChildByTag( ESLT_GROUND ) );
    	if( pMap )
    	{
    		switch( pMap->getMapOrientation() )
    		{
    		case CCTMXOrientationIso:	// 等轴视距模式
    			{
    				CIsometricController* pCtrl = CIsometricController::create();
    				pCtrl->bindScene( this );
    				addChild( pCtrl, 0, ESLT_CONTROL );
    			}
    			break;
    		case CCTMXOrientationOrtho:
    			break;
    		case CCTMXOrientationHex:
    			break;
    		default: 
    			CCLOG( "[CMainScene] unknown map orientation: %n.", pMap->getMapOrientation() );
    			break;
    		}
    	}
    
    	// 后续层扩展
    
    	return true;
    }
    
    // 卸载场景
    bool CMainScene::unloadScene()
    {
    	CCLOG( "[CMainScene] Prepare unload tilemap." );
    
    	// 卸载该层所有子节点
    	removeAllChildrenWithCleanup( true );
    
    	return true;
    }
    
    // 加载地图
    bool CMainScene::loadMap( const char* map_file )
    {
    	CCLOG( "[CMapLayer] Prepare load tilemap from: %s.", map_file );
    	
    	// 加载一个新的瓦片地图场景节点(TMX是一种瓦片地图的保存格式)
    	CCTMXTiledMap *map = CCTMXTiledMap::create( map_file );
    	if( !map )
    	{
    		CCLOG( "[CMapLayer] load tilemap failed, please check file: %s.", map_file );
    		return false;
    	}
    
    	// 添加进地形层
    	addChild( map, 0, ESLT_GROUND );
    
    #ifdef _DEBUG
    	// 输出新地图的基本信息
    	CCLOG( "" );
    	CCLOG( "********************* new tmx tile map info ********************" );
    
    	// 原始尺寸
    	CCSize conSize = map->getContentSize();
    	CCLOG( "[CMapLayer] Content size: %.2f, %.2f.", conSize.width, conSize.height );
    
    	// 瓦片排布
    	CCSize mapSize = map->getMapSize();
    	CCLOG( "[CMapLayer] Map size: %.2f, %.2f.", mapSize.width, mapSize.height );
    
    	// 瓦片尺寸
    	CCSize tilSize = map->getTileSize();
    	CCLOG( "[CMapLayer] Tile size: %.2f, %.2f.", tilSize.width, tilSize.height );
    
    	// 地图对象信息
    	CCArray* pChildren = map->getChildren();
    	if( pChildren )
    	{
    		int nLayerCount = 0;		// 层数量
    		int nObjectTotalCount = 0;	// 总的对象数
    		CCObject* pObj = NULL;
    		CCARRAY_FOREACH( pChildren, pObj ) 
    		{ 
    			++nObjectTotalCount;
    			CCTMXLayer* layer = dynamic_cast<CCTMXLayer*>(pObj);
    			if( layer ) 
    			{ 
    				CCLOG( "[CMapLayer] TMX Layer: name:%s.", layer->getLayerName() );
    				++nLayerCount;
    			}
    		}
    		CCLOG( "[CMapLayer] Total object count: %d.", nObjectTotalCount );
    		CCLOG( "[CMapLayer] Total mtx layer count: %d.", nLayerCount );
    	}
    
    	// 对象组信息
    	CCArray* pGroups = map->getObjectGroups();
    	if( pGroups && pGroups->count() > 0 )
    	{
    		CCTMXObjectGroup* pObjectGroup = NULL;
    		CCObject* pObj = NULL;
    		CCARRAY_FOREACH( pGroups, pObj )
    		{
    			pObjectGroup = dynamic_cast<CCTMXObjectGroup*>( pObj );
    			if( pObjectGroup )
    			{
    				CCLOG( "[CMapLayer] Object group: name: %s.", pObjectGroup->getGroupName() );
    			}
    		}
    	}
    
    	CCLOG( "" );
    
    #endif
    
    	// 对地图节点设定位置
    	map->setPosition( 0, 0 );
    
    	return true;
    }
    
    // PKG
    
    $#include "../Scene/MainScene.h"
    
    // 场景层
    typedef enum 
    {
    	ESLT_GROUND,	// 地面
    	ESLT_OBJECT1,	// 对象层
    	ESLT_OBJECT2,	// 对象层
    	ESLT_OBJECT3,	// 对象层
    	ESLT_SKY,		// 天空层
    	ESLT_GUI,		// 界面层
    	ESLT_CONTROL,	// 控制层
    	ESLT_FORCE32 = 0x7FFFFFFF
    } EnSceneLayerTag;
    
    class CMainScene : public CCScene
    {
    	// 加载场景
    	bool loadScene( const char* tmx_file );
    
    	// 卸载场景
    	bool unloadScene();
    	
    	// 静态create方法实现
    	static CMainScene* create();
    };
    

       二. 控制类

    // -------------------------------------------------------
    // 文件名: IsometricController.h
    // 描  述: 等轴视距控制器
    // 日  期: 11/3/2013 
    // 作  者: KevinYuen
    // 版  权: Copyright (C) 2011 - All Rights Reserved
    // 备  注: 
    // -------------------------------------------------------
    
    #ifndef _ISOMETRICCONTROLLER_H_
    #define _ISOMETRICCONTROLLER_H_
    
    #include "cocos2d.h"
    
    USING_NS_CC;
    
    class CIsometricController : public CCLayer
    {
    public:
    
    	// 析构
    	~CIsometricController();
    	
    public:
    
    	// 初始化
    	virtual bool init();
    
    	// 进入(所属场景被设置为当前运行场景时调用)
    	virtual void onEnter();
    
    	// 销毁(所属场景被从当前运行场景取下时调用)
    	virtual void onExit();
    
    	// 静态create方法实现
    	CREATE_FUNC( CIsometricController );
    
    public:
    
    	// 绑定场景
    	void bindScene( CCScene* scene );
    
    	// 获取绑定场景
    	CCScene* getScene() const;
    
    protected:
    
    	// 注册事件监听
    	void registerWithTouchDispatcher( void );
    
    	// 触摸拖动
    	virtual void ccTouchesMoved( CCSet *pTouches, CCEvent *pEvent );
    
    	// 触摸开始
    	virtual bool ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent );
    
    	// 触摸结束
    	virtual void ccTouchEnded( CCTouch *pTouch, CCEvent *pEvent );
    
    private:
    
    	// 构造函数
    	CIsometricController();
    
    	CCScene* m_pBindScene;	// 绑定场景
    };
    
    #endif
    
    // CPP
    #include "IsometricController.h"
    #include "MainScene.h"
    
    // 构造函数
    CIsometricController::CIsometricController(): m_pBindScene( NULL )
    {
    
    }
    
    // 析构
    CIsometricController::~CIsometricController()
    {
    
    }
    
    // 初始化
    bool CIsometricController::init()
    {
    	bool ret = CCLayer::init();
    	if( !ret ) return false;
    
    	// 代码添加预留
    
    	return true;
    }
    
    // 进入(所属场景被设置为当前运行场景时调用)
    void CIsometricController::onEnter()
    {
    	CCLayer::onEnter();
    	
    	// 设置响应触摸
    	setTouchEnabled( true );
    }
    
    // 销毁(所属场景被从当前运行场景取下时调用)
    void CIsometricController::onExit()
    {
    	// 解除场景绑定
    	bindScene( NULL );
    
    	// 设置响应触摸
    	setTouchEnabled( false );
    
    	CCLayer::onExit();
    }
    
    // 绑定场景
    void CIsometricController::bindScene( CCScene* scene )
    {
    	m_pBindScene = scene;
    }
    
    // 获取绑定场景
    CCScene* CIsometricController::getScene() const
    {
    	return m_pBindScene;
    }
    
    // 注册事件监听
    void CIsometricController::registerWithTouchDispatcher( void )
    {
    	CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate( this, 0, false );
    	CCLayer::registerWithTouchDispatcher();
    }
    
    // 获取指定节点坐标的地块索引
    // 方法来自网络
    CCPoint tilePosFromLocation( const CCPoint& location, CCTMXTiledMap* tileMap )
    {
    	// Tilemap position must be subtracted, in case the tilemap position is scrolling
    	CCPoint pos = ccpSub(location, tileMap->getPosition() );
    	float halfMapWidth = tileMap->getMapSize().width * 0.5f;
    	float mapHeight = tileMap->getMapSize().height;
    	float tileWidth = tileMap->getTileSize().width;
    	float tileHeight = tileMap->getTileSize().height;
    	CCPoint tilePosDiv = CCPointMake(pos.x / tileWidth, pos.y / tileHeight);
    	float inverseTileY = mapHeight - tilePosDiv.y;
    	// Cast to int makes sure that result is in whole numbers
    	float posX = (int)(inverseTileY + tilePosDiv.x - halfMapWidth);
    	float posY = (int)(inverseTileY - tilePosDiv.x + halfMapWidth);
    	// make sure coordinates are within isomap bounds
    	posX = MAX(0, posX);
    	posX = MIN(tileMap->getMapSize().width - 1, posX);
    	posY = MAX(0, posY);
    	posY = MIN(tileMap->getMapSize().height - 1, posY);
    	return CCPointMake(posX, posY);
    }
    
    // 触摸拖动
    void CIsometricController::ccTouchesMoved( CCSet *pTouches, CCEvent *pEvent )
    {
    	CCTouch *touch = (CCTouch*)pTouches->anyObject();
    	CCPoint diff = touch->getDelta();
    
    	CCTMXTiledMap* pTmxMap = dynamic_cast<CCTMXTiledMap*>( m_pBindScene->getChildByTag( ESLT_GROUND ) );
    	if( pTmxMap )
    	{
    		CCPoint currentPos = pTmxMap->getPosition();
    		pTmxMap->setPosition( ccpAdd(currentPos, diff) );
    	}
    }
    
    // 触摸开始
    bool CIsometricController::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )
    {
    	// 这里必须返回真,才会有后续的Move和End消息
    	return true;
    }
    
    // 触摸结束
    void CIsometricController::ccTouchEnded( CCTouch *pTouch, CCEvent *pEvent )
    {
    	if( !m_pBindScene ) return;
    
    	CCPoint touchLocation = convertTouchToNodeSpace( pTouch );
    
    	// 检测是否有TMX瓦块地图
    	CCTMXTiledMap* pTmxMap = dynamic_cast<CCTMXTiledMap*>( m_pBindScene->getChildByTag( ESLT_GROUND ) );
    	if( pTmxMap )
    	{
    		CCTMXLayer* layer = pTmxMap->layerNamed("ground");
    		CCSprite* sprite = layer ? layer->tileAt( tilePosFromLocation( touchLocation, pTmxMap ) ) : NULL;
    		if( sprite )
    			sprite->setVisible( false );
    	}
    }
    

       三. 脚本调用

        与上章不同,我重新创建了一个新的脚本,抛弃模版例子,命名为main.lua.

    --------------------------------------------------------
    --文件名: main.lua
    --描  述: 主脚本
    --日  期: 11/3/2013 
    --作  者: KevinYuen
    --版  权: Copyright (C) 2011 - All Rights Reserved
    --备  注: 
    ---------------------------------------------------------
    
    -- 跟踪绑定执行函数发生错误的信息并输出
    function __G__TRACKBACK__(msg)
        print("----------------------------------------")
        print("LUA ERROR: " .. tostring(msg) .. "\n")
        print(debug.traceback())
        print("----------------------------------------")
    end
    
    -- 主函数
    local function main()
    
    	-- 创建主场景
    	local scene = CMainScene:create();
    	if not scene then
    		print( "Create main scene failed!" );
    		return;
    	end
    	
    	-- 加载测试场景
    	-- 引擎对CCTMXTiledMap有限制,每一层只能有一个瓦块包,也就是一张纹理
    	if not scene:loadScene( "TilesMap/Isotile_2.tmx" ) then
    		print( "Load tilemap failed!" );
    		return;
    	end
    			
    	-- 设定为当前场景并执行
        CCDirector:sharedDirector():runWithScene( scene );
    
    end
    
    -- 执行脚本函数并捕获错误信息
    -- 函数原型: xpcall( 调用函数, 错误捕获函数 );
    xpcall(main, __G__TRACKBACK__)
    

     运行结果

    下一步会增加对象层,实现场景对象的添加 :)

  • 相关阅读:
    Java 多态
    HDFS读写原理
    HDFS详解
    Servlet基础
    Tomcat
    HTTP简介
    JDBC技术
    final、finally和finalize
    java 中的权限修饰符
    进程、线程、线程状态、多线程实现方法
  • 原文地址:https://www.cnblogs.com/KevinYuen/p/2954207.html
Copyright © 2011-2022 走看看