zoukankan      html  css  js  c++  java
  • cocos2d-x游戏开发系列教程-坦克大战游戏之子弹和地图碰撞

    上篇文章实现了坦克与地图碰撞的检测,

    这篇我们继续完成子弹和地图的碰撞检测。


    1.先设计一个子弹类Bullet,如下所示:

    class Bullet : public CCSprite
    {
    public:
    	Bullet();
    	~Bullet();
    
    	static Bullet* createBulletWithTank(Tank* tank);
    	void initBulletWithTank(Tank* tank);
    	bool fire();
    	virtual void update(float delta);
    	void bulletBoom();
    
    private:
    	bool isFlying;
    	Tank* mTank;
    	TileMapInfo* mTileMapInfo;
    	float stepX, stepY;
    };


    在上面

    static Bullet* createBulletWithTank(Tank* tank);
    void initBulletWithTank(Tank* tank);

    分别是创建子弹的静态方法和初始化子弹的方法,他们都有一个共同参数tank,

    用来表示他们所属的坦克。

    fire()方法用来被坦克开火的时候调用,

    然后在update方法中控制子弹移动以及碰撞检测。

    在撞击后通过调用bulletBoom来引爆子弹,炸毁周围的砖块等。


    2.在构造函数中初始化

    Bullet::Bullet() :isFlying(false)
    {
    }
    isFlying指示子弹是否在飞行中,初始为false表示没有飞行。


    3.void Bullet::initBulletWithTank(Tank* tank)函数的实现:

    void Bullet::initBulletWithTank(Tank* tank)
    {
    	mTank = tank;
    	mTileMapInfo = mTank->getTileMapInfo();
    
    	initWithSpriteFrameName("bullet.png");
    	mTileMapInfo->getTileMap()->addChild(this);
    	setVisible(false);
    }

    在Bullet类中我们用mTank成员变量存储了他所属的坦克,

    用mTIleMapInfo存储了他所属的地图。

    用bullet.png精灵帧初始化了精灵。

    然后把子弹加入了地图层中

    然后把子弹设置为不可见。


    4.继续实现静态方法Bullet* Bullet::createBulletWithTank(Tank* tank)

    Bullet* Bullet::createBulletWithTank(Tank* tank)
    {
    	Bullet* bullet = new Bullet();
    	bullet->initBulletWithTank(tank);
    	bullet->autorelease();
    
    	return bullet;
    }
    可以看到实现非常简单,创建一个Bullet类实例,然后用tank参数初始化,

    最后加入自动释放列表,然后返回子弹类指针。


    5.是先fire()函数,控制子弹运动方向等:

    bool Bullet::fire()
    {
    	if (!isFlying)
    	{
    		isFlying = true;
    		setVisible(true);
    		setPosition(mTank->getPosition());
    		//设置子弹运行方向
    		int Rotation = mTank->getRotation();
    		switch (Rotation)
    		{
    		case 0:
    			stepX = 0.0f, stepY = 2.0f;
    			break;
    		case 90:
    			stepX = 2.0f, stepY = 0.0f;
    			break;
    		case 180:
    			stepX = 0.0f, stepY = -2.0f;
    			break;
    		case 270:
    			stepX = -2.0f, stepY = 0.0f;
    			break;
    		default:
    			break;
    		}
    		scheduleUpdate();
    	}
    
    	return isFlying;
    }

    可以看到,先判断子弹是否在飞行中,如果不在飞行中被调用了,

    则先设置飞行状态isFlying为飞行中。

    然后设置子弹初始位置为坦克所在的位置,

    然后根据坦克旋转的方向,来设置子弹的运行方向分量——stepX和stepY。

    最后调用scheduleUpdate()启动update定时器。这样显示每一帧动画的时候,

    就会调用我们的update()函数。


    6.下面看看怎么实现update()函数:

    void Bullet::update(float delta)
    {
    	CCSprite::update(delta);
    	//设置移动后的位置
    	setPosition(ccp(getPositionX() + stepX, getPositionY() + stepY));
    
    	//检测是否有碰撞
    	CCRect rect = boundingBox();
    	if (mTileMapInfo->collisionTest(rect))
    	{
    		unscheduleUpdate();
    		setVisible(false);
    		isFlying = false;
    		//引爆子弹
    		bulletBoom();
    	}
    }
    上面先设置了子弹根据分量stepX和stepY移动后的位置。

    然后获取自己在地图上的CCRect,然后传入地图信息类中的碰撞检测函数。

    如果碰撞了,就调用unscheduleUpdate()来取消update()定时器,

    然后设置子弹为不可见,飞行状态设置为false,调用bulletBoom()来引爆子弹。


    7.下面实现Bullet类中最后一个成员函数bulletBoom(),来看看它怎么引爆子弹的:

    void Bullet::bulletBoom()
    {
    	CCRect rect = boundingBox();
    	CCSize mapSize = mTileMapInfo->getTileMap()->getContentSize();
    
    	if (rect.getMinX() < 0 || rect.getMaxX() >= mapSize.width ||
    		rect.getMinY() < 0 || rect.getMaxY() >= mapSize.height)
    		return ;
    
    	CCTMXLayer* tmxLayer = mTileMapInfo->getTileMap()->layerNamed("layer_0");
    	CCSize tileSize = tmxLayer->getMapTileSize();
    
    	//调整Y轴位tmx地图中的Y轴
    	float MinY = mapSize.height - rect.getMinY();
    	float MaxY = mapSize.height - rect.getMaxY();
    
    	//将与子弹碰撞的墙壁tileWall图块删除
    	CCPoint pt = ccp((int)rect.getMinX() / tileSize.width,(int)(MinY / tileSize.height));
    	if (gidToTileType[tmxLayer->tileGIDAt(pt)] == tileWall)
    		tmxLayer->setTileGID(gidToTileType[tileNone], pt);
    
    	pt = ccp((int)rect.getMinX() / tileSize.width,(int)(MaxY / tileSize.height));
    	if (gidToTileType[tmxLayer->tileGIDAt(pt)] == tileWall)
    		tmxLayer->setTileGID(gidToTileType[tileNone], pt);
    
    	pt = ccp((int)rect.getMaxX() / tileSize.width,(int)(MinY / tileSize.height));
    	if (gidToTileType[tmxLayer->tileGIDAt(pt)] == tileWall)
    		tmxLayer->setTileGID(gidToTileType[tileNone], pt);
    
    	pt = ccp((int)rect.getMaxX() / tileSize.width,(int)(MaxY / tileSize.height));
    	if (gidToTileType[tmxLayer->tileGIDAt(pt)] == tileWall)
    		tmxLayer->setTileGID(gidToTileType[tileNone], pt);
    }

    先获取了自己的位置rect和地图的尺寸mapSIze。

    首先判断了自己是否跑到了地图外边。如果在地图外边就直接返回。


    然后获取了layer_0层的地图信息,将屏幕中的Y轴转换成tmx地图中的Y轴,

    然后判断子弹四个角所接触的图块,如果图块是墙壁,那么获取墙壁的GID都设置成tileNone,这样就相当于炸毁墙壁图块。


    8.最后我们需要在Tank类型初始化我们的子弹才能看到子弹运行效果,在坦克类中:

    void Tank::initTankWithTankType(const char* tankTypeName, TileMapInfo* tileMapInfo)
    {
    	initWithSpriteFrameName(tankTypeName);
    	mTileMapInfo = tileMapInfo;
    
    	//将坦克放入地图层中
    	mTileMapInfo->getTileMap()->addChild(this);
    
    	//缩放到合适大小
    	CCTMXTiledMap* tmxTileMap = mTileMapInfo->getTileMap();
    	CCSize tileSize = tmxTileMap->getTileSize();
    	CCSize tankSize = getContentSize();
    	//比地图上砖块小一点
    	setScale((tileSize.height * 2-4) / (tankSize.height));
    
    	//初始化坦克的子弹
    	mBullet = Bullet::createBulletWithTank(this);
    }
    可以看到很简单在最下面一行,给mBullet成员函数调用Bullet::createBulletWithTank(this)初始化了子弹。

    然后我们还要在Tank类的命中响应函数中调用开火命令:

    void Tank::command(enumOrder order)
    {
    	float stepX = 0.0f;
    	float stepY = 0.0f;
    	static float fRotation = 0.0f;
    
    	switch (order)
    	{
    	case cmdNothing:
    		break;
    	case cmdGoUP:
    		stepY = 1.0f;
    		fRotation = 0.0f;
    		break;
    	case cmdGoDown:
    		stepY = -1.0f;
    		fRotation = 180.0f;
    		break;
    	case cmdGoLeft:
    		stepX = -1.0f;
    		fRotation = 270.0f;
    		break;
    	case cmdGoRight:
    		stepX = 1.0f;
    		fRotation = 90.0f;
    		break;
    	case cmdFire:
    		//调用子弹开火
    		mBullet->fire();
    		break;
    	default:
    		break;
    	}
    
    	//检测地图上的碰撞
    	CCRect rect = this->boundingBox();
    	if (!mTileMapInfo->collisionTest(CCRectMake(rect.getMinX() + stepX, 
    		rect.getMinY() + stepY, rect.size.width, rect.size.height)))
    	{
    		setPositionX(getPositionX() + stepX);
    		setPositionY(getPositionY() + stepY);
    	}
    
    	//根据运行方向旋转坦克
    	setRotation(fRotation);
    }
    case cmdFire:
    //调用子弹开火
    mBullet->fire();
    break;

    容易看到,只加上这一句就能实现开火效果了。


    下面我们编译运行程序,看看效果:



    完整实现的代码下载地址如下:

    http://download.csdn.net/detail/yincheng01/6760931

  • 相关阅读:
    MQCONN failed (Reason = 2277)
    MQ打开队列模式 input和input_exclusive
    mq 消息最大长度 最大是100M
    Easyui Datagrid的Rownumber行号显示问题
    ajax请求时session已过期处理方案
    seafile Windows Memcached
    seafile 接口频度控制
    你的旧船票能否搭上这艘巨轮?——解读近5年大数据产业发展规划
    第一章 输入/输出知识
    It looks like you don't have a C compiler and make utility installed. 错误
  • 原文地址:https://www.cnblogs.com/new0801/p/6177289.html
Copyright © 2011-2022 走看看