cocos2dx实例开发之经典坦克

2023-05-16

小时候红白机上玩的的经典90坦克,看起来简单,做起来其实有点复杂,这里用原版素材还原了一个简版

预览

在这里插入图片描述

工程结构

在这里插入图片描述
游戏架构
在这里插入图片描述
包括场景:

  • 欢迎界面,主菜单
  • 游戏场景

步骤

菜单场景

对于图片,音乐,动画提前做缓存,提高后面使用效率

// 预加载资源(暂且使用同步模式)
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("img/tank/tank.plist", "img/tank/tank.png");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("img/item/item.plist", "img/item/item.png");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("img/tank/blast.plist", "img/tank/blast/blast.png");

SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/levelstarting.wav");
SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/gamewin.wav");
SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/gameover.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/bonus.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/brickhit.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/eexplosion.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/fexplosion.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/ice.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/life.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/moving.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/nmoving.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/shieldhit.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/shoot.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/steelhit.wav");
SimpleAudioEngine::getInstance()->preloadEffect("sound/tbonushit.wav");

Animation* player_born_animation = Animation::create();
player_born_animation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("shield1.png"));
player_born_animation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("shield2.png"));
player_born_animation->setDelayPerUnit(0.2);
AnimationCache::getInstance()->addAnimation(player_born_animation, "player_born_animation");

Animation* enemy_born_animation = Animation::create();
for (int i = 1; i <= 4; i++)
    enemy_born_animation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("born" + std::to_string(i) + ".png"));
enemy_born_animation->setDelayPerUnit(0.2);
AnimationCache::getInstance()->addAnimation(enemy_born_animation, "enemy_born_animation");

游戏场景

场景中地图、坦克、子弹、道具都是子元素,每个元素都有对应的行为和状态

数据结构

地图

地图是由tmx文件导入的,提前在tiled编辑器里导出,每个方格是地图块的最小单位
方格类型包括:

  • 空白
  • 土砖
  • 钢板
  • 草地

不同方格属性不同,比如是否能让坦克通过,是否可以被子弹破坏

// 加载游戏地图(tile地图必须保证tmx文件跟图片资源相对目录)
std::string map_name = "img/map/Round" + std::to_string(round) + ".tmx";
initWithTMXFile(map_name);
//    tile_map->setScale(kScaleFactor); // this works to make map looks bigger

// print map info
Size map_size = getContentSize();
Size map_array = getMapSize();
// tmx地图的方格必须用行列值重新计算,getTileSize()是不准确的
Size tile_size = Size(map_size.width / map_array.width, map_size.height / map_array.height);

由于地图加载场景中,尺寸大小会有缩放,所以需要精确计算每个方块的大小

玩家坦克

class Player : public Sprite
{
public:
    virtual bool init();
    
    CREATE_FUNC(Player);
    
public:
    void initWithType(PlayerType player_type);
    void setGameScene(GameScene* game_scene);
    void move(float tm);
    Bullet* shootSingle(); // 射击一次,产生一颗子弹
    Vector<Bullet*> shootDouble(); // 射击一次,产生双子弹
    void fetchItem(ItemType item_type); // 拾取道具
    void destroy(); // 玩家over
    
public:
    void setSize(Size size);
    void setDirection(JoyDirection direction);
    JoyDirection m_head_direction; // 坦克朝向
    float m_bullet_interval;
    bool m_moving;
    PlayerStatus m_status;
    PlayerWeapon m_weapon;
    
private:
    GameScene* m_game_scene = nullptr; // like callback
    Size m_size;
};
  • 切换状态
  • 移动
  • 变换方向
  • 拾取道具
  • 开炮(不同子弹类型)

敌方坦克

enum EnemyType
{
    NORMAL,  // 普通坦克
    ARMOR,   // 装甲车
    SPEED    // 迅捷坦克
};

enum EnemyStatus
{
    ESIMPLE, // 普通
    ESHIELD  // 无敌
};
class Enemy : public Sprite
{
public:
    virtual bool init();
    
    CREATE_FUNC(Enemy);
    
    void initWithType(EnemyType enemy_type);
    void setSize(Size size);
    
public:
    void setDirection(JoyDirection direction);
    JoyDirection m_head_direction;
    void move(float tm);
    void changeDirection();
    Bullet* shoot();
    void hit();
    void die();
    int m_life;
    
    EnemyType m_type;
    bool m_moving;
    EnemyStatus m_status;
    
private:
    Size m_size;
    float m_speed;
};
  • 不同类型(普通、游击、装甲)
  • 移动
  • 切换方向
  • 开炮

子弹

enum BulletType
{
    BASE, // 单子弹
    POWER // 双子单
};
class Bullet : public cocos2d::Sprite
{
public:
    virtual bool init();
    void initWithDirection(JoyDirection direction, BulletType bullet_type = BASE); // 初始化方向和纹理
    CREATE_FUNC(Bullet);
    
public:
    BulletType m_type; // 子弹类型
    bool m_hit_flag; // 标记子弹是否已击中
    
private:
    void move(float tm);
    JoyDirection m_direction;
};
  • 不同火力
  • 移动
  • 撞击

道具

enum ItemType
{
    ACTIVE, // 帽子
    STAR,  // 火力星
    BOMB,   // 炸弹
    SHOVEL,  // 铲子
    CLOCK,  // 定时
    MINITANK // 命
};

class Item : public Sprite
{
public:
    virtual bool init();
    
    CREATE_FUNC(Item);
    void initWithType(ItemType item_type);

public:
    ItemType m_type;
    
};
  • 不同类型(帽子、火力、炸弹、铲子、定时、命)
  • 出现
  • 被拾取

虚拟摇杆

专门实现了一个摇杆和射击的中间控制层,通过回调函数的方式施加于游戏场景,其中摇杆控制是稍微复杂一点的
基本思路:确定好摇杆中心的情况下,通过三角函数关系计算跟随触点与坐标横轴的角度,根据角度范围划分具体的方向,另外,在判定射击的时候要处理好左右屏和触摸先后的顺序

// 获取以p1为圆心,p2p1与x轴正方向的弧度值
float Joypad::calcRad(Point p1, Point p2)
{
    float xx = p2.x - p1.x;
    float yy = p2.y - p1.y;
    
    // 斜边
    float xie = sqrt(pow(xx, 2) + pow(yy, 2));
    // yy >= 0 弧度在于 0 到 π 之间。(0~180°)
    // yy < 0 弧度在于 π 到 2π 之间。(180°~360°)
    float rad = yy >= 0 ? (acos(xx / xie)) : (PI * 2 - acos(xx / xie));
    return rad;
}

// 得到与角度对应的半径为R的圆上一坐标点, 相对值
Vec2 Joypad::getAnglePosition(float R, float rad)
{
    return Point(R * cos(rad), R * sin(rad));
}

如图所示
在这里插入图片描述

void Joypad::onTouchesMoved(const std::vector<Touch*>& touches, Event* event)
{
//    CCLOG("onTouchMoved");
    
    if (!m_can_move)
        return;
    
    Point visible_origin = Director::getInstance()->getVisibleOrigin();
    Size visible_size = Director::getInstance()->getVisibleSize();
    
    // 某一时刻可能是一个或两个触点
    Point point1 = touches.front()->getLocation();
    Point point2 = touches.back()->getLocation();
    
    Point wheel_center = m_wheel->getPosition();
    float wheel_radius = m_wheel->getContentSize().width / 2;
//    float stick_radius = m_stick->getContentSize().width / 2;
    
    // 区分左右触点,可能是同一个
    Point left_point;
    Point right_point;
    if (point1.x < point2.x)
    {
        left_point = point1;
        right_point = point2;
    }
    else
    {
        left_point = point2;
        right_point = point1;
    }
    
    // 只有左触点在左半边屏才判断摇杆
    if (left_point.x < visible_origin.x + visible_size.width / 2)
    {
        Point point = left_point;
        // 判断两个圆心的距离是否大于外圈半径
        float distance = sqrt(pow(point.x - wheel_center.x, 2) + pow(point.y - wheel_center.y, 2));
        
        float rad = calcRad(wheel_center, point);
        if (distance >= wheel_radius)
        {
            // 摇杆中心不超出外圈范围
            m_stick->setPosition(wheel_center + getAnglePosition(wheel_radius, rad));
        }
        else
            m_stick->setPosition(point); // 摇杆跟随触点
        
        // 换算成角度,根据键类型确定方向,将方向控制信号传给游戏场景
        float angle = rad * 180.0 / PI;
        if (m_type == KEY4)
        {
            // 加入控制死区,只有圆心偏移距离够长才换方向
            JoyDirection direction;
            if (distance >= wheel_radius / 5)
            {
                // 靠近轴90度范围
                if ((angle >= 0 && angle < 45) || (angle >= 315 && angle < 360))
                    direction = RIGHT;  // 右
                else if (angle >= 45 && angle < 135)
                    direction = UP;    // 上
                else if (angle >= 135 && angle < 225)
                    direction = LEFT;  // 左
                else if (angle >= 225 && angle < 315)
                    direction = DOWN;  // 下
            }
            else
                direction = NONE;
            
            // callback
            if (m_game_scene)
                m_game_scene->onEnumDirection(direction);
        }
        else if (m_type == KEY8)
        {
            // 加入控制死区,只有圆心偏移距离够长才换方向
            JoyDirection direction;
            if (distance >= wheel_radius / 5)
            {
                // 靠近轴45度范围
                if ((angle >= 0 && angle < 22.5) || (angle >= 337.5 && angle < 360))
                    direction = RIGHT; // 右
                else if (angle >= 22.5 && angle < 67.5)
                    direction = RIGHT_UP; // 右上
                else if (angle >= 67.5 && angle < 112.5)
                    direction = UP; // 上
                else if (angle >= 112.5 && angle < 157.5)
                    direction = LEFT_UP; // 左上
                else if (angle >= 157.5 && angle < 202.5)
                    direction = LEFT; // 左
                else if (angle >= 202.5 && angle < 247.5)
                    direction = LEFT_DOWN; // 左下
                else if (angle >= 247.5 && angle < 292.5)
                    direction = DOWN; // 下
                else if (angle >= 292.5 && angle < 337.5)
                    direction = RIGHT_DOWN; // 右下
            }
            else
                direction = NONE;
            
            // callback
            if (m_game_scene)
                m_game_scene->onEnumDirection(direction);
        }
        else if (m_type == KEYANY)
        {
            // callback
            if (m_game_scene)
                m_game_scene->onAngleDirection(angle);
        }
    }
}
  • 中间的stick要跟随触点,但是有界限
  • 触点放开就自动归位

玩家控制

玩家控制包括方向键移动和开火射击

void GameScene::onEnumDirection(JoyDirection direction)
{
//    CCLOG("GameScene onEnumDirection: %d", direction);
    
    static JoyDirection pre_direction = NONE;
    // 只有方向改变时才给玩家坦克发控制指令
    if (direction != pre_direction)
    {
        // TODO: 每次重置为最近的整点方块中心位置,减少被卡住的概率
        m_player1->setDirection(direction);
        pre_direction = direction;
    }
}

void GameScene::onFireBtn(bool is_pressed)
{
//    CCLOG("GameScene onFireBtn: %s", is_pressed ? "yes" : "no");
    
    static bool pre_press_status = false;
    if (is_pressed != pre_press_status)
    {
        // 调度子弹,同时考虑单发和i连发
        if (is_pressed)
        {
            // TODO: every moment make sure only limit number bullets in the screen
            // FIXME: timer would wait at least one delay interval, here shoot a bullet at one btn click
            if (m_player_bullets.empty())
            {
                if (m_player1->m_weapon == SINGLE_GUN)
                {
                    Bullet* bullet = m_player1->shootSingle();
                    addChild(bullet, kMapZorder);
                    m_player_bullets.pushBack(bullet);
                }
                else if (m_player1->m_weapon == DOUBLE_GUN)
                {
                    Vector<Bullet*> double_bullets = m_player1->shootDouble();
                    for (Bullet* bullet : double_bullets)
                    {
                        addChild(bullet, kMapZorder);
                        m_player_bullets.pushBack(bullet);
                    }
                }
            }
            
            schedule(schedule_selector(GameScene::emitPlayerBullet), m_player1->m_bullet_interval);
        }
        else
            unschedule(schedule_selector(GameScene::emitPlayerBullet));
            
        pre_press_status = is_pressed;
    }
}

以上是游戏场景的几个回调函数,供摇杆类调用控制玩家

  • 移动中如果遇到障碍就无法沿当前方向继续移动
  • 移动遇到道具自动拾取并生效
  • 可以在移动中开火
  • 长按射击可以连续开火
  • 每次只允许有限的子弹,下一发要等到前一发结束

敌方坦克行为

敌方坦克随机出现在地图上方出生点,随机切换方向,随机开火,同一批次生成的坦克有数量限制,总坦克数量有限制

void GameScene::generateEnemy(float tm)
{
    // 最后剩下的则停掉坦克生成
    if (m_enemies.size() == m_enemy_count)
    {
        unschedule(schedule_selector(GameScene::generateEnemy));
        return;
    }
    
    // 当前还有完整的一批,则不产生
    if (m_enemies.size() >= kEnemyBatchTankCount)
        return;
    
    Size map_size = m_battle_field->getContentSize();
    Size map_array = m_battle_field->getMapSize();
    // tmx地图的方格必须用行列值重新计算,getTileSize()是不准确的
    Size tile_size = Size(map_size.width / map_array.width, map_size.height / map_array.height);

    // 根据概率生成敌方坦克
    float tank_type_factor = CCRANDOM_0_1();
    EnemyType enemy_type;
    if (tank_type_factor < 0.6)
        enemy_type = NORMAL;
    else if (tank_type_factor >= 0.6 && tank_type_factor < 0.9)
        enemy_type = ARMOR;
    else
        enemy_type = SPEED;
    
    Enemy* enemy = Enemy::create();
    enemy->initWithType(enemy_type);
    enemy->setSize(tile_size * 2 * kTankSizeFactor);
    
    // 随机生成位置,顶部三个空位
    float tank_pos_factor = CCRANDOM_0_1();
    if (tank_pos_factor <= 1.0 / 3)
        enemy->setPosition(m_battle_field->getPositionX() + tile_size.width,
                           m_battle_field->getPositionY() + map_size.height - tile_size.height);
    else if (tank_pos_factor >= 1.0 / 3 && tank_pos_factor < 2.0 / 3)
        enemy->setPosition(m_battle_field->getPositionX() + map_size.width / 2,
                           m_battle_field->getPositionY() + map_size.height - tile_size.height);
    else
        enemy->setPosition(m_battle_field->getPositionX() + map_size.width - tile_size.width,
                           m_battle_field->getPositionY() + map_size.height - tile_size.height);
    
    // 随机生成方向
    float tank_direction_factor = CCRANDOM_0_1();
    if (tank_direction_factor < 0.25)
        enemy->setDirection(UP);
    else if (tank_direction_factor >= 0.25 && tank_direction_factor < 0.5)
        enemy->setDirection(DOWN);
    else if (tank_direction_factor >= 0.5 && tank_direction_factor < 0.75)
        enemy->setDirection(LEFT);
    else
        enemy->setDirection(RIGHT);
    
    // 根据定时决定初始是否可以移动
    if (m_is_clock)
        enemy->m_moving = false;
    
    addChild(enemy, kMapZorder);
    m_enemies.pushBack(enemy);
}
  • 固定时间间隔检查调度
  • 移动中遇到障碍无法沿当前方向继续移动
  • 可以在移动中开火
  • 不同坦克类型移动速度不同
  • 不同坦克射击子弹频率不同

子弹生成

子弹实际是由玩家或地方坦克射击产生的,但是需要在场景中调度

void GameScene::emitPlayerBullet(float tm)
{
    // 小技巧,如果子弹消失了则快速发射一颗,所以子弹发射频率会随着碰撞调整
    if (m_player_bullets.empty())
    {
        if (m_player1->m_weapon == SINGLE_GUN)
        {
            Bullet* bullet = m_player1->shootSingle();
            addChild(bullet, kMapZorder);
            m_player_bullets.pushBack(bullet);
        }
        else if (m_player1->m_weapon == DOUBLE_GUN)
        {
            Vector<Bullet*> double_bullets = m_player1->shootDouble();
            for (Bullet* bullet : double_bullets)
            {
                addChild(bullet, kMapZorder);
                m_player_bullets.pushBack(bullet);
            }
        }
    }
}

void GameScene::emitEnemyBullet(float tm)
{
    for (Enemy* enemy : m_enemies)
    {
        // 不同的敌人坦克根据类型,发射子弹频率不同
        float enemy_shoot_factor = CCRANDOM_0_1();
        if (enemy->m_type == NORMAL && enemy_shoot_factor >= 0.5
            || enemy->m_type == ARMOR && enemy_shoot_factor >= 0.2
            || enemy->m_type == SPEED && enemy_shoot_factor >= 0.7)
        {
            Bullet* bullet = enemy->shoot();
            addChild(bullet, kMapZorder);
            m_enemy_bullets.pushBack(bullet);
        }
    }
}
  • 固定时间间隔检查调度
  • 射击后自动飞行
  • 射出时出现在坦克头部
  • 射出时的方向跟坦克方向相同
  • 碰撞到不同障碍物有对应行为(普通子弹只能打土砖,火力子弹可以床钢板)

道具生成

道具是玩家射击到装甲车地方坦克时,根据概率随机选择类型出现在地图任意位置

void GameScene::generateItem()
{
    SimpleAudioEngine::getInstance()->playEffect("sound/tbonushit.wav");
    
    Size map_size = m_battle_field->getContentSize();
    Size map_array = m_battle_field->getMapSize();
    // tmx地图的方格必须用行列值重新计算,getTileSize()是不准确的
    Size tile_size = Size(map_size.width / map_array.width, map_size.height / map_array.height);
    
    // 随机类型
    ItemType item_type;
    float item_type_factor = CCRANDOM_0_1();
    if (item_type_factor < 1.0 / 6)
        item_type = ACTIVE;
    else if (item_type_factor >= 1.0 / 6 && item_type_factor < 2.0 / 6)
        item_type = STAR;
    else if (item_type_factor >= 2.0 / 6 && item_type_factor < 3.0 / 6)
        item_type = BOMB;
    else if (item_type_factor >= 3.0 / 6 && item_type_factor < 4.0 / 6)
        item_type = SHOVEL;
    else if (item_type_factor >= 4.0 / 6 && item_type_factor < 5.0 / 6)
        item_type = CLOCK;
    else
        item_type = MINITANK;
    
    Item* item = Item::create();
    item->initWithType(item_type);
    
    // 随机位置
    float item_posx_factor = CCRANDOM_0_1();
    float pos_x = m_battle_field->getPositionX() + (map_size.width - tile_size.width * 2) * item_posx_factor;
    float item_posy_factor = CCRANDOM_0_1();
    float pos_y = m_battle_field->getPositionY() + (map_size.height - tile_size.height * 2) * item_posy_factor;
    item->setPosition(pos_x, pos_y);
    
    addChild(item, kItemZorder);
    m_items.pushBack(item);
}
  • 固定时间间隔检查调度
  • 会有闪烁特效
  • 不同类型被拾取后有对应效果

碰撞检查

有多种类型的碰撞检查

  • 判断总部老鹰是否被击毁
  • 判断敌方坦克是否被射击,死亡还是减血,掉落道具
  • 判断玩家坦克是否被射击
  • 判断玩家坦克移动是否遇到障碍
  • 判断敌方坦克移动是否遇到障碍
  • 判断玩家子弹对于场景的碰撞破坏
  • 判断敌方子弹对于场景的碰撞破坏

具体逻辑都卸载update函数,保证每帧都会检测,及时反映

其中由于需要说明的是,对于地图中不同砖块被子弹碰撞或被坦克碰撞的逻辑,需要根据坐标定位到方块gid号,进而判断类型来做对应的处理,另外如果拾取到铲子道具,需要对地图中土砖变换为钢板,做方块类型的变更

bool isBulletCollide(Rect bounding_box, BulletType bullet_type); // 子弹的碰撞
bool isTankCollide(Rect bounding_box, JoyDirection direction); // 带方向坦克的碰撞
bool isEagleHurt(Rect bounding_box);

void protectEagle(); // 用钢板把老鹰围起来
void unprotectEagle(); // 老鹰恢复为土砖围起来

元素管理

场景中的元素需要及时的回收和释放,避免内存浪费

  • 出了地图边界的子弹销毁
  • 碰撞的子弹销毁
  • 被破坏的方块销毁
  • 被击毁的地方坦克销毁
  • 被击毁的玩家坦克销毁

游戏状态

如果地方坦克全部被击毁,则游戏胜利;如果玩家坦克命数用完或者总部老鹰被击毁则游戏结束;当前关卡通过后会进入下一关

void GameScene::gameWin()
{
    CCLOG("game win");
    
    SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/gamewin.wav", false);
    
    // 切换关卡,通关后从第一关开始
    int next_round = m_round + 1; // 关卡提升
    if (next_round > kTotalRound)
        next_round = 1;
    
    // FIXME: should delay before next round
    Scene* scene = GameScene::createScene(next_round);
    TransitionScene* transition_scene = TransitionFade::create(0.0, scene);
    Director::getInstance()->replaceScene(transition_scene);
}

void GameScene::gameOver()
{
    CCLOG("game over");
    SimpleAudioEngine::getInstance()->playEffect("sound/gameover.wav", false);
    m_is_over = true;
    
    // 游戏结束的标签
    Point visible_origin = Director::getInstance()->getVisibleOrigin();
    Size visible_size = Director::getInstance()->getVisibleSize();
    
    Label *gameover_label = Label::createWithTTF("game over", "fonts/Marker Felt.ttf", 24);
    gameover_label->setColor(Color3B::WHITE);
    gameover_label->setPosition(visible_origin.x + visible_size.width / 2,
                                visible_origin.y - 30);
    addChild(gameover_label, kLevelSplashZorder); // 分数浮层在最上方
    
    // 播放动画,飞入画面
    auto move_to = MoveTo::create(1.0, Point(visible_origin.x + visible_size.width / 2,
                                             visible_origin.y + visible_size.height / 2));
    gameover_label->runAction(move_to);
}

效果图

在这里插入图片描述

在这里插入图片描述

后记

可以考虑扩展成双人联网合作模式
技术实现思路:

  • 通过局域网连接
  • 主玩家建立内嵌http websocket服务器接收从玩家的连接
  • 互相之间通过websocket长连接通信
  • 游戏逻辑在服务端计算,图形渲染在客户端各自渲染

代码

csdn:经典坦克
github:经典坦克

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cocos2dx实例开发之经典坦克 的相关文章

  • FFmpeg音频处理——音频混合、拼接、剪切、转码

    接触FFmpeg有一段时间了 xff0c 它是音视频开发的开源库 xff0c 几乎其他所有播放器 直播平台都基于FFmpeg进行二次开发 本篇文章来总结下采用FFmpeg进行音频处理 xff1a 音频混合 音频剪切 音频拼接与音频转码 采用
  • Android三种方式截取任意界面屏幕

    一 使用MediaProjectionManager Android5 0之后 xff0c 开放截取屏幕的API xff0c 也就是利用MediaProjectionManager创建VirtualDisplay xff0c 传入与Imag
  • ijkplayer基于rtsp直播延时的深度优化

    现在ijkPlayer是许多播放器 直播平台的首选 xff0c 相信很多开发者都接触过ijkPlayer xff0c 无论是Android工程师还是iOS工程师 我曾经在Github上的ijkPlayer开源项目上提问过 xff1a 视频流
  • C++ 程序编译过程

    前言 C语言的编译链接过程要把我们编写的一个c程序 xff08 源代码 xff09 转换成可以在硬件上运行的程序 xff08 可执行代码 xff09 xff0c 需要进行编译和链接 编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程
  • 自旋锁实现机理 spin_lock

    自旋锁的概念 自旋锁 xff08 spin lock xff09 是一种典型的对临界资源进行互斥访问的手段 xff0c 它是基于系统原子操作为基础 xff0c 自旋锁最多只能被一个可执行线程持有 xff0c 如果一个执行线程试图获得一个被已
  • 数据结构:图之DFS与BFS的复杂度分析

    BFS的复杂度分析 BFS是一种借用队列来存储的过程 xff0c 分层查找 xff0c 优先考虑距离出发点近的点 无论是在邻接表还是邻接矩阵中存储 xff0c 都需要借助一个辅助队列 xff0c v个顶点均需入队 xff0c 最坏的情况下
  • python中的os.path.dirname(__file__)的使用

    os path dirname file 返回脚本的路径 xff0c 但是需要注意一下几点 1 必须是实际存在的 py文件 xff0c 如果在命令行执行 xff0c 则会引发异常NameError name 39 file 39 is no
  • 北斗模块学习之初

    BD定义 xff1a 北斗卫星导航系统 xff08 BeiDou xff08 COMPASS xff09 NavigationSatellite System xff09 是中国正在实施的自主发展 独立运行的全球卫星导航系统 系统建设目标是
  • 关于Boost库和STL标准模板库

    一 关于STL 首先 xff0c 关于stl xff0c 最近也看了不少关于stl的博客 往大概的讲 xff0c stl即一种标准的模板库 xff0c 同时 xff0c 它也是静态库 xff0c 它存在的目的即是为了实现代码的服用性 xff
  • c语言char* 转char[]方法

    vector lt char gt splite by delim char host name char a 1000 strcpy a host name char p 61 strtok a split vector lt char
  • STL常用容器对比

    STL的常用容器大致有以下8个 xff1a 1 vector vector是一种动态数组 xff0c 在内存中具有连续的存储空间 xff0c 支持快速随机访问 由于具有连续的存储空间 xff0c 所以在插入和删除操作方面 xff0c 效率比
  • C 语言 stm32 无符号8位转换为int类型 uchar 转换为 int

    我做的是一个无线鼠标 xff0c stm32用nRF24L01无线传输模块发送数据时 xff0c 是传输无符号8位 uchar 三个数据是角度值 xff0c 有正负 直接使用无符号数据的话负数会出错 xff0c 在接收端要把数据恢复为整数
  • 常见IMU的性能比较

    型号gyr零偏稳定性gyr量程acc零偏稳定性acc量程HZ价格其他说明 EG320N xff08 epson xff09 http www canalgeomatics com wp content uploads 2020 06 oem
  • strcat函数用法的一点看法

    最近在刷题时碰到了strcat这个函数 xff0c 一开始没怎么理解它的用法 xff0c 出了错 xff0c 现在写点自己的理解吧 首先来看两个小程序 代码一 xff1a include lt iostream gt using names
  • CMake教程——QT项目使用CMake

    文章目录 1 Basic Cmake Based Project2 Executable VS Library3 Every module has its own CMakeList txt in its folder3 1 不推荐的做法
  • Proteus ISIS仿真软件中英文元件名称对照

    定时 计数器的使用方法 xff1a CLK xff1a 计数和测频状态时 xff0c 数字波的输入端 xff08 counter enable CE xff1a 计数使能端 xff1b 通过属性设置高还是低有效 无效暂停计数 RST 复位端
  • STL详解

    STL简介 编程的抽象发展 xff1a 面向过程 to 基于对象 to
  • 双目立体视觉 I:标定和校正

    点击上方 AI公园 xff0c 关注公众号 xff0c 选择加 星标 或 置顶 作者 xff1a Ali Yasin Eser 编译 xff1a ronghuaiyang 导读 双目立体校正和标定 大家好 xff01 今天我们将讨论什么是立
  • 4_竞赛无人机基本自动飞行支持函数与导航控制函数解析——零基础学习竞赛无人机搭积木式编程

    竞赛无人机基本自动飞行支持函数与导航控制函数解析 基本自动飞行支持函数 void basic auto flight support void 根据前面几讲的介绍 xff0c 要想实现无人机的自动飞行 xff0c 单依靠姿态自稳 高度控制远
  • Ubuntu20.04/Ubuntu22.04 配置VScode+Opencv+cmake(C++)

    下面介绍Ubuntu20 04下安装opencv xff0c 当然Ubuntu22 04也适用 xff0c 然后将opencv链接到VsCode 先主体按照 gt 点我 xff1a 链接1 lt 的第一点进行安装 xff0c 但是特别注意

随机推荐

  • makefile和cmake

    目录 作用优点cmake 作用 makefile关系到了整个工程的编译规则 一个工程中的源文件不计其数 xff0c 其按类型 功能 模块分别放在若干个目录中 xff0c makefile定义了一系列的规则来指定 xff0c 哪些文件需要先编
  • Linux查看线程的堆栈信息

    1 使用top命令 xff0c 查找pid 2 显示线程 xff0c 查找线程tid ps mp pid o THREAD tid time sort rn 3 将线程id转化为16进制0xtid printf 34 x n 34 tid
  • MPU6050 简介

    目录 关于MPU6050芯片 关于小板 关于厂家和DATASHEET 关于漂移 关于角加速度还是角速度 关于精度和量程 xff08 可调 xff0c 可选 xff09 关于功耗 xff0c 陀螺仪 43 加速器工作电流 xff1a 3 8m
  • 银河麒麟V10操作系统安装putty和cutecom和网络调试助手(mNetAssist)

    银河麒麟V10操作系统安装putty和cutecom和网络调试助手 xff08 mNetAssist xff09 安装Putty 需要连接网络 sudo apt get install putty 安装Cutecom 需要连接网络 sudo
  • STM32串口中断接收和中断发送

    STM32串口USART1中断接收和中断发送 先贴出中断函数 void USART1 IRQHandler void if USART GetITStatus USART1 USART IT RXNE 61 RESET USART Clea
  • ICP(Iterative Closest Point迭代最近点)算法学习笔记

    背景 xff1a 博主从百度百科开始学习icp算法 xff0c 主要是后期加得学习笔记 xff08 红色部分 xff09 ICP算法 xff1a 以点集对点集 xff08 PSTPS xff09 配准方法为基础 xff0c 他们阐述了一种曲
  • linux设备上的Onvif 实现21:解决大华摄像头无法使用问题

    好长时间没有再写该系列文章了 xff0c 最近刚好摸索着解决了大华摄像头无法使用问题 xff0c 记录下来 xff0c 应该对其他博友有所帮助 之前虽然写了一大堆文章说明了如何使用gsoap连接摄像头 xff0c 但这是针对一台海康的摄像头
  • NRF24L01工作原理(发送接收通道地址)解读 图示

    NRF24L01工作原理 xff08 发送接收通道地址 xff09 解读 图示 NRF24l01工作原理 xff08 发送接收通道地址 xff09 网上说明不清晰 xff0c 特制作本说明 xff0c xff08 制作 xff1a 流浪的蛙
  • Realsense D435i 使用

    工作之后才发现问题不是单线程地来找你 xff0c 而是多线程并发地涌向你 D435i是一款良心传感器 xff0c 美中不足的是你拿不到广角图像 虽然现在不负责传感器测试了 xff08 老大布置什么 xff0c 打工的就去做什么就好了 xff
  • Kalibr标定d435i

    figure it out 最近准备使用realsense d435i xff0c 先对其进行标定 整体环境是基于ROS的 xff0c 因为Kalibr是在ROS环境下 大致过程如下 xff1a imu标定 gt 双目标定 gt 双目 43
  • cout函数未定义问题和函数隐形类型转换

    cout函数未定义的一些问题 xff1a 看上面的图片 xff0c 有个朋友说 xff0c 按照上面using和修改了iostream为什么还是显示未定呢 xff1f 到底怎么办呢 xff1f 解决 xff1a include lt ios
  • C++ primer plus

    https github com lilinxiong cppPrimerPlus six https github com lilinxiong cppPrimerPlus six 最近在看C 43 43 primer plus这本书 x
  • ROS编译过程中的错误

    一 catkin make编译错误 xff08 1 xff09 Could not find a package configuration file provided by 34 gazebo ros control 34 with an
  • 算法导论学习笔记-2

    第二章 xff1a 函数的增长 2 1渐近记号 记号 定义 xff1a g n 61 f n 存在正常数c1 c2和n0 xff0c 使对所有n n0 xff0c 有0 c1 g n f n c2 g n xff1b 满足该定义 xff0c
  • C++理论复习之运算符重载

    C 43 43 运算符重载 重载操作符的一般格式 xff1a Type operator 43 Type A Type B 可重载与不可重载的操作符列表 操作符重载的注意事项 重载操作符必须有一个类类型的操作数 xff0c 不能重载内置类型
  • 计算机网络概述

    OSI参考模型 国际标准化组织 I S O 开发了开放式系统互联 O S I 参考模型 xff0c 以促进计算机系统的开放互联 开放式互联就是可在多个厂家的环境中支持互联 很少有产品是完全的O S I 模式 xff1b 相反 xff0c 其
  • PCL:如何自定义一个点云PointT类型

    1 xff0c 使用基础点云类型 include 34 pcl point types h 34 include 34 pcl impl instantiate hpp 34 include 34 foo h 34 include 34 i
  • IP地址

    ip地址的分类 A类地址 设计IPv4 A类地址的目的是支持巨型网络 xff0c 因为对规模巨大网络的需求很小 xff0c 因此开发了这种结构使主机地址数很大 xff0c 而严格限制可被定义为A类网络的数量 一个A类I P地址仅使用第一个8
  • 数据存储大端小端模式的理解

    Big Endian和Little Endian的定义如下 xff1a 1 Little Endian就是低位字节排放在内存的低地址端 xff0c 高位字节排放在内存的高地址端 2 Big Endian 就是高位字节排放在内存的低地址端 x
  • cocos2dx实例开发之经典坦克

    小时候红白机上玩的的经典90坦克 xff0c 看起来简单 xff0c 做起来其实有点复杂 xff0c 这里用原版素材还原了一个简版 预览 工程结构 游戏架构 包括场景 xff1a 欢迎界面 xff0c 主菜单游戏场景 步骤 菜单场景 对于图