Qt:击球游戏

举报
何其不顾四月天 发表于 2020/12/29 00:36:45 2020/12/29
4.2k+ 0 0
【摘要】 近期做的一个小项目:Qt击球游戏,相当于二次开发增加附加功能。 已有功能: JSON文件读取;球类生成;球桌生成;碰撞规则; 新增功能: JSON文件新增节点读取;球袋功能;击球功能;动能功能;设计模式:必须 组合模式+其他模式(适配器); 母球分裂功能; 开发环境: Ubuntu+Qt5.10.1+QtCreator4.6.0 功能实现:   &nb...

近期做的一个小项目:Qt击球游戏,相当于二次开发增加附加功能。

已有功能:


      JSON文件读取;
      球类生成;
      球桌生成;
      碰撞规则;
  
 

新增功能:


 

      JSON文件新增节点读取;
      球袋功能;
      击球功能;
      动能功能;
      设计模式:必须 组合模式+其他模式(适配器);
      母球分裂功能;
  
 

开发环境:

Ubuntu+Qt5.10.1+QtCreator4.6.0

 

功能实现:

                球袋功能:


      "pockets": [
       {"position":{"x":50,"y":50}},
       {"position":{"x":195,"y":195}},
       {"position":{"x":5,"y":295},"radius":20},
       {"position":{"x":100,"y":100},"radius":25}
  
 

               球袋采用组合模式:


      struct pocket
      {
      double x;
      double y;
      double radius;
      };  //球袋属性结构体
  
 

组合模式:抽象类,采用未保护模式


      class CompontPocket
      {
      public:
       CompontPocket() {}
      virtual void Addpocket(CompontPocket *) = 0;
      virtual void Removepocket(CompontPocket *) = 0;
      virtual pocket Output() = 0;
      virtual int getVecCount() = 0;
      virtual QVector <CompontPocket *> getvetor() = 0;
      virtual void render(QPainter& painter) = 0;
       /* virtual CompontPocket * getItem(QVector<CompontPocket *>::iterator it) =0;*/
      virtual void PocketSetValue(double x,double y ,int rad)=0;
      protected:
       pocket  p_pocket;
      };
  
 

组合模式:叶子类 实现单一元素操作


      class LeafPocket : public CompontPocket
      {
      public:
       LeafPocket():CompontPocket() {}
      void Addpocket(CompontPocket *) {return ;}
      void Removepocket(CompontPocket *) {return ;}
      pocket Output() {  return p_pocket;}
      int getVecCount() {return 0;}
       QVector <CompontPocket *> getvetor() {  QVector<CompontPocket * > a; return  a; }
       // CompontPocket * getItem(QVector<CompontPocket *>::iterator it) { CompontPocket * a; a = NULL; return a; }
      void PocketSetValue(double x,double y ,int rad)
       {
       p_pocket.x = x;
       p_pocket.y = y;
       p_pocket.radius = rad;
       }
      void render(QPainter& painter)
       {
       QVector2D m_point;
       m_point.setX(p_pocket.x);
       m_point.setY(p_pocket.y);
      // use our colour
       painter.setBrush(Qt::black);
      // circle centered
       painter.drawEllipse(m_point.toPointF(), p_pocket.radius, p_pocket.radius);
       }
      };
  
 

组合模式:枝节点类 实现组合容器操作


      class CompositePockets: public CompontPocket
      {
      public:
      // CompositePockets():CompontPocket() {}
      void Addpocket(CompontPocket* m_Pocket)
       {
       m_Pockets.push_back(m_Pocket);
       }
      void Removepocket(CompontPocket * p_Pocket)
       {
      for(QVector<CompontPocket *>::iterator it=m_Pockets.begin();it!=m_Pockets.end();++it)
       {
      if(p_Pocket == *it)
       {
       m_Pockets.erase(it);
      break;
       }
       }
       }
      pocket Output()
       {
      /* for(QVector<CompontPocket *>::iterator it = m_Pockets.begin();it<m_Pockets.end();++it )
       {
       CompontPocket * poc = *it;
       poc->Output();
       }*/
      return p_pocket;
       }
      int getVecCount()
       {
      int Count = m_Pockets.size();
      return Count;
       }
      QVector<CompontPocket *> getvetor()
       {
      return m_Pockets;
       }
      /* CompontPocket * getItem(QVector<CompontPocket *>::iterator it)
       {
       CompontPocket * Buf =*it;
       return Buf;
       }*/
      void PocketSetValue(double x,double y ,int rad)
       {
       p_pocket.x = x;
       p_pocket.y = y;
       p_pocket.radius = rad;
       }
      void render(QPainter& painter)
       {
      for(auto it = m_Pockets.begin();it!= m_Pockets.end();++it)
       {
       CompontPocket * pockets = *it;
       pockets->render(painter);
       }
       }
      private:
       QVector <CompontPocket *> m_Pockets;  //球袋的集合
      };
      extern CompositePockets  p_CompositePockets;
  
 

击球功能: 适配器模式/实现比较简单,强行用设计者模式


      /*需适配者*/
      class ballarm
      {
      public:
      virtual void render(QPainter& painter ,QVector2D m_startpos,QVector2D m_endpos) = 0;
      };
  
 

      /*适配元素*/
      class ColorBallarm
      {
      public:
      void DrawBallArm(QPainter& painter ,QVector2D m_startpos,QVector2D m_endpos)
       {
       QPen pen;
       pen.setColor(Qt::white);   //颜色:白色
       pen.setWidth(5); //宽度:5
       painter.setPen(pen);
       painter.drawLine(m_startpos.toPointF(),m_endpos.toPointF());  //画线
       }
      };
  
 

      /*适配器*/
      class UseBallarm: public ballarm
      {
      public:
       UseBallarm():m_ColorBallarm(new ColorBallarm) {}
       ~UseBallarm();
      void render(QPainter &painter, QVector2D m_startpos, QVector2D m_endpos)
       {
       m_ColorBallarm->DrawBallArm(painter,m_startpos,m_endpos);
       }
      private:
       ColorBallarm * m_ColorBallarm;
      };
  
 

      设计者模式这一块也为初次接触,公司项目比较单一,很少接触这些,熬了一晚上,才看懂,稍微理解了一点组合模式和适配者模式。


      组合模式:抽象类,叶子类,枝节点类。
      抽象类只提供虚函数接口,不作具体实现。
      叶子类,实现单一元素的操作,设置属性,元素的具现之类的。
      枝节点类,元素集合体,可作为根节点与枝节点。实现对集合的操作,元素的增删改。元素容器一般在私有类里边,作为对元素的保护。对外提供接口时,尽量使用变量,少使用指针,作为安全保护。
      我没有采用保护者模式,保护者模式,抽象类,只抽像单一元素操作,不提供枝节点类的操作接口。
  
 

      适配者模式:我目前的理解,就拿电脑显示接口举例,电脑只提供了对外输出的VGA视频接口,但是目前需要HDMI接口的电脑,将电脑和HDMI转接头差分为两个元素。
      两个适配器组成的类,电脑作为基础类,HDMI作为适配元素,将两个元素组合在一起,将变成了一个新的类,提供HDMI接口的电脑。
  
 

JSON元素读取:

提供的JSON数据格式:


      "balls" : [
       {"colour":"white","position":{"x":50,"y":50},"velocity":{"x":20,"y":50},"mass":2,"radius":30,"strength":1e5},
       {"colour":"red","position":{"x":150,"y":60},"velocity":{"x":-20,"y":20},"mass":2,"radius":100,"strength":1e5},
       {"colour":"blue","position":{"x":550,"y":320},"velocity":{"x":-150,"y":80},"mass":2,"radius":15,"strength":1e5},
       {"colour":"yellow","position":{"x":450,"y":200},"velocity":{"x":100,"y":-80},"mass":1,"radius":10,"strength":1e4},
       {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5},
       {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5}
       ]
  
 


      "balls" : [
       {"colour":"white","position":{"x":50,"y":50},"velocity":{"x":20,"y":50},"mass":2,"radius":30,"strength":1e5},
       {"colour":"red","position":{"x":150,"y":60},"velocity":{"x":-20,"y":20},"mass":2,"radius":100,"strength":1e5},
       {"colour":"blue","position":{"x":550,"y":320},"velocity":{"x":-150,"y":80},"mass":2,"radius":15,"strength":1e5},
       {"colour":"yellow","position":{"x":450,"y":200},"velocity":{"x":100,"y":-80},"mass":1,"radius":10,"strength":1e4},
       {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5,
      "balls":[
       {"colour":"red","position":{"x":-10,"y":0},"velocity":{"x":100,"y":10},"mass":1,"radius":10},
       {"colour":"red","position":{"x":20,"y":0},"velocity":{"x":100,"y":10},"mass":1,"radius":30},
       {"colour":"red","position":{"x":0,"y":-10}},
       {"position":{"x":0,"y":10}, "mass":2,"strength":1e4,
      "balls":[
       {"colour":"blue","strength":1e3,
      "balls":[
       {"colour":"red", "strength":1e4}
       ]
       }
       ]
       }
       ]
       },
       {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5,
      "balls":[
       {"colour":"red","position":{"x":-10,"y":40},"velocity":{"x":100,"y":10},"mass":1,"radius":10},
       {"colour":"red","position":{"x":60,"y":0}},
       {"colour":"red","position":{"x":70,"y":-10}}
       ]
       }
         ]
  
 

  JSON数据的读取:


      QJsonObject loadConfig() {
      // load json from config file
      QFile conf_file(config_path);
       conf_file.open(QIODevice::ReadOnly | QIODevice::Text);
       QString content = conf_file.readAll();
       conf_file.close();
       QJsonObject config = QJsonDocument::fromJson(content.toUtf8()).object();
      return config;
      }
  
 

      // for each of our balls, construct them
       QJsonArray ballData = m_conf->value("balls").toArray();
      if(!stageFlag)
       {
      for (const auto& item : ballData)
       {
       QJsonObject t = item.toObject();
       m_builder->addBall(t);
       }
       }else  CreatBall(ballData);
  
 

      void GameDirector::CreatBall(QJsonArray ballData) //球的创建
      {
      for (const auto& item : ballData)
       {
      // PosIndex ++;
       m_BallPos_int[p_balltype] = PosIndex;  //同一级别中,上一次所记录的最大值
       QJsonObject t = item.toObject();
       QJsonArray  t_Arry =t.value("balls").toArray();
      if(!t_Arry.count())   { m_builder->addBall(t);}
      else
       {
       m_BallPos_int[p_balltype] = PosIndex;
       m_builder->addBall(t);
       p_balltype ++;
       PosIndex =m_BallPos_int[p_balltype];
       CreatBall(t_Arry);  //递归寻找每一级的球
       }
       PosIndex ++;
       BallNum ++;
       m_BallPos_int[p_balltype] = PosIndex;
       }
      if(p_balltype>0) p_balltype --; else ;  //每一次退出时,等级减一,回到上一级别
       PosIndex = m_BallPos_int[p_balltype];  //得到上一级上一次最后一个球的位置
      }
  
 

JSON数据的读取没什么可说的,在这比较有点难度的地方是,JSON数据作为提供球类的数据元素,球有一个属性为包含属性,每个球 被球袋吃掉或者碰撞破碎的时候,将所包含的子球都show出来。而子球的个数不限。但在整体框架中,尽量维持原来的框架属性不变,只能增加功能作为嵌套实现。实现时给球类增加了两个属性,等级与位置。等级每一个球所在的等级,也就对应JSON数据的数据层级,位置为每个球所在等级的位置。

为此建立了两个容器,一个容器,放置,当前所有显示球类元素的容器,一个容器作为记录第二层及以后球类的元素。

球类元素的改造前与改造后。


       StageOneBall(QColor colour, QVector2D position,
       QVector2D velocity, double mass, int radius,double strength) : Ball(colour, position, velocity, mass, radius,strength) {}
  
 

       StageOneBall(QColor colour, QVector2D position,
       QVector2D velocity, double mass, int radius,double strength,int Balltype,int TypeIndex) :
       Ball(colour, position, velocity, mass, radius,strength,Balltype,TypeIndex) {}
  
 

容器:


      struct ParentStr
      {
        int  balltype ;
        int  ballindex;
      };
      extern int p_balltype;   //层级
      extern ParentStr *m_Partent;  //父类索引属性
      extern QMap<ParentStr *,Ball *>  childball;  //第二级以后球的集合
  
 
std::vector<Ball*>* m_balls;//当前显示球类容器
 

第二级以后球类集合,为Map容器,Key,记录对应球类元素的所对应它的父类的等级与位置。当一个父类球被删除掉的时候,提取出当前删除球所对应的等级与位置,作为key,遍历第二级及以后球的集合的容器,将对应的球类元素添加到当前球类元素显示容器。

口袋吃球函数:


      bool PocketEatingBall(QVector2D pos,double ballrad)
      {
        bool status;
         QVector<CompontPocket *> Buf_m_pockets = p_CompositePockets.getvetor();
        for (auto it =Buf_m_pockets.begin();it!= Buf_m_pockets.end();it++)
       {
       CompontPocket * Buf_Compont = *it;
       pocket  a_pocket =  Buf_Compont->Output();
      double Log2 = a_pocket.radius*a_pocket.radius-((a_pocket.x-pos.x())*(a_pocket.x-pos.x())+(a_pocket.y-pos.y())*(a_pocket.y-pos.y()));
      double rad2 = ballrad*ballrad;
      double value = Log2-rad2;
      if(value<0) {status = 0;}
      else  { status = 1; break;}
       }
      return status;
      }
  
 

球类元素删除函数:


      void Game::ballsDelItem()
      {
      int i =0,a=0;
      if(j == 0) return;
      for(a = 0;a<j;++a)
       {
      for (std::vector<Ball *>::iterator it = m_balls->begin();it!=m_balls->end();)
       {
       Ball * DelBall = *it;
      if(i== buf[a])
       {
       DelBallbuf[a][0] = DelBall->getBalltype();
       DelBallbuf[a][1] = DelBall->getBalltypeIndex();
       it =  m_balls->erase(it);
      // qDebug()<<"Del A Ball"<<m_balls->size()<<DelBallbuf[a][0]<<DelBallbuf[a][1];
       }
      else ++it;
       ++i;
       }
       }
       NextBallsShow(j);
      }
  
 

子球显示函数:


      void Game::NextBallsShow(int Num)
      {
      for(int a = 0;a<Num;++a)
       {
      for (QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it)
       {
      if(it.key()->balltype==DelBallbuf[a][0]&&it.key()->ballindex==DelBallbuf[a][1])
       {
      // qDebug()<<"No instert"<<m_balls->size();
       m_balls->push_back(it.value());
      // qDebug()<<"Instert "<<m_balls->size();
       }
       }
       }
      }
  
 

动能功能:


      bool Game::EnergyCalculation(Ball* ballA) //碰撞能量计算
      {
      bool status;  //状态值
      float ballMass  = ballA->getMass();
      float ballStrength = ballA->getstrength();
      float ballRadius  = ballA->getRadius();
       QVector2D preCollisionVelocity  = ballA->getVelocity()+=changeVec;
       QVector2D deltaV = changeVec;
      float energyOfCollision = ballMass*deltaV.lengthSquared();  //能量计算公式
      // qDebug()<<energyOfCollision << deltaV.lengthSquared()<<changeVec.x()<<changeVec.y();
      int balltype = ballA->getBalltype();
      int ballIndex = ballA->getBalltypeIndex();
      int numComponentBalls = 0;
      for(QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it)  //得到当前球里边包含子球的个数
       {
       Ball * componentBall = *it;
      if(componentBall->getBalltypeIndex() == balltype && componentBall->getBalltypeIndex() == ballIndex)
       {
       numComponentBalls++;
       }
       }
      if(ballStrength<energyOfCollision)  //承受质量小于碰撞能量
       {
      float energyPerBall = energyOfCollision/numComponentBalls; //子球的能量
       deltaV.normalize();
      QVector2D pointOfCollision((-deltaV)*ballRadius);
      //for each component ball
      for(QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it)
       {
       Ball * componentBall = *it;
      if(componentBall->getBalltypeIndex() == balltype && componentBall->getBalltypeIndex() == ballIndex)
       {
       QVector2D buf = componentBall->getPosition()-pointOfCollision;
       buf.normalize();
       QVector2D componentBallVelocity = preCollisionVelocity + sqrt(energyPerBall/componentBall->getMass())*buf; //子球速度的计算
       componentBall->changeVelocity(componentBallVelocity);  //子球速度的设置
       }
       }
       status = 1;
       }else status =0;
      return status;
      }
  
 

文章来源: blog.csdn.net,作者:何其不顾四月天,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/u011218356/article/details/80396571

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。