Qt:击球游戏

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

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

已有功能:


  
  1. JSON文件读取;
  2. 球类生成;
  3. 球桌生成;
  4. 碰撞规则;

新增功能:


 

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

开发环境:

Ubuntu+Qt5.10.1+QtCreator4.6.0

 

功能实现:

                球袋功能:


  
  1. "pockets": [
  2. {"position":{"x":50,"y":50}},
  3. {"position":{"x":195,"y":195}},
  4. {"position":{"x":5,"y":295},"radius":20},
  5. {"position":{"x":100,"y":100},"radius":25}

               球袋采用组合模式:


  
  1. struct pocket
  2. {
  3. double x;
  4. double y;
  5. double radius;
  6. }; //球袋属性结构体

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


  
  1. class CompontPocket
  2. {
  3. public:
  4. CompontPocket() {}
  5. virtual void Addpocket(CompontPocket *) = 0;
  6. virtual void Removepocket(CompontPocket *) = 0;
  7. virtual pocket Output() = 0;
  8. virtual int getVecCount() = 0;
  9. virtual QVector <CompontPocket *> getvetor() = 0;
  10. virtual void render(QPainter& painter) = 0;
  11. /* virtual CompontPocket * getItem(QVector<CompontPocket *>::iterator it) =0;*/
  12. virtual void PocketSetValue(double x,double y ,int rad)=0;
  13. protected:
  14. pocket p_pocket;
  15. };

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


  
  1. class LeafPocket : public CompontPocket
  2. {
  3. public:
  4. LeafPocket():CompontPocket() {}
  5. void Addpocket(CompontPocket *) {return ;}
  6. void Removepocket(CompontPocket *) {return ;}
  7. pocket Output() { return p_pocket;}
  8. int getVecCount() {return 0;}
  9. QVector <CompontPocket *> getvetor() { QVector<CompontPocket * > a; return a; }
  10. // CompontPocket * getItem(QVector<CompontPocket *>::iterator it) { CompontPocket * a; a = NULL; return a; }
  11. void PocketSetValue(double x,double y ,int rad)
  12. {
  13. p_pocket.x = x;
  14. p_pocket.y = y;
  15. p_pocket.radius = rad;
  16. }
  17. void render(QPainter& painter)
  18. {
  19. QVector2D m_point;
  20. m_point.setX(p_pocket.x);
  21. m_point.setY(p_pocket.y);
  22. // use our colour
  23. painter.setBrush(Qt::black);
  24. // circle centered
  25. painter.drawEllipse(m_point.toPointF(), p_pocket.radius, p_pocket.radius);
  26. }
  27. };

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


  
  1. class CompositePockets: public CompontPocket
  2. {
  3. public:
  4. // CompositePockets():CompontPocket() {}
  5. void Addpocket(CompontPocket* m_Pocket)
  6. {
  7. m_Pockets.push_back(m_Pocket);
  8. }
  9. void Removepocket(CompontPocket * p_Pocket)
  10. {
  11. for(QVector<CompontPocket *>::iterator it=m_Pockets.begin();it!=m_Pockets.end();++it)
  12. {
  13. if(p_Pocket == *it)
  14. {
  15. m_Pockets.erase(it);
  16. break;
  17. }
  18. }
  19. }
  20. pocket Output()
  21. {
  22. /* for(QVector<CompontPocket *>::iterator it = m_Pockets.begin();it<m_Pockets.end();++it )
  23. {
  24. CompontPocket * poc = *it;
  25. poc->Output();
  26. }*/
  27. return p_pocket;
  28. }
  29. int getVecCount()
  30. {
  31. int Count = m_Pockets.size();
  32. return Count;
  33. }
  34. QVector<CompontPocket *> getvetor()
  35. {
  36. return m_Pockets;
  37. }
  38. /* CompontPocket * getItem(QVector<CompontPocket *>::iterator it)
  39. {
  40. CompontPocket * Buf =*it;
  41. return Buf;
  42. }*/
  43. void PocketSetValue(double x,double y ,int rad)
  44. {
  45. p_pocket.x = x;
  46. p_pocket.y = y;
  47. p_pocket.radius = rad;
  48. }
  49. void render(QPainter& painter)
  50. {
  51. for(auto it = m_Pockets.begin();it!= m_Pockets.end();++it)
  52. {
  53. CompontPocket * pockets = *it;
  54. pockets->render(painter);
  55. }
  56. }
  57. private:
  58. QVector <CompontPocket *> m_Pockets; //球袋的集合
  59. };
  60. extern CompositePockets p_CompositePockets;

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


  
  1. /*需适配者*/
  2. class ballarm
  3. {
  4. public:
  5. virtual void render(QPainter& painter ,QVector2D m_startpos,QVector2D m_endpos) = 0;
  6. };

  
  1. /*适配元素*/
  2. class ColorBallarm
  3. {
  4. public:
  5. void DrawBallArm(QPainter& painter ,QVector2D m_startpos,QVector2D m_endpos)
  6. {
  7. QPen pen;
  8. pen.setColor(Qt::white); //颜色:白色
  9. pen.setWidth(5); //宽度:5
  10. painter.setPen(pen);
  11. painter.drawLine(m_startpos.toPointF(),m_endpos.toPointF()); //画线
  12. }
  13. };

  
  1. /*适配器*/
  2. class UseBallarm: public ballarm
  3. {
  4. public:
  5. UseBallarm():m_ColorBallarm(new ColorBallarm) {}
  6. ~UseBallarm();
  7. void render(QPainter &painter, QVector2D m_startpos, QVector2D m_endpos)
  8. {
  9. m_ColorBallarm->DrawBallArm(painter,m_startpos,m_endpos);
  10. }
  11. private:
  12. ColorBallarm * m_ColorBallarm;
  13. };

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


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

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

JSON元素读取:

提供的JSON数据格式:


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


  
  1. "balls" : [
  2. {"colour":"white","position":{"x":50,"y":50},"velocity":{"x":20,"y":50},"mass":2,"radius":30,"strength":1e5},
  3. {"colour":"red","position":{"x":150,"y":60},"velocity":{"x":-20,"y":20},"mass":2,"radius":100,"strength":1e5},
  4. {"colour":"blue","position":{"x":550,"y":320},"velocity":{"x":-150,"y":80},"mass":2,"radius":15,"strength":1e5},
  5. {"colour":"yellow","position":{"x":450,"y":200},"velocity":{"x":100,"y":-80},"mass":1,"radius":10,"strength":1e4},
  6. {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5,
  7. "balls":[
  8. {"colour":"red","position":{"x":-10,"y":0},"velocity":{"x":100,"y":10},"mass":1,"radius":10},
  9. {"colour":"red","position":{"x":20,"y":0},"velocity":{"x":100,"y":10},"mass":1,"radius":30},
  10. {"colour":"red","position":{"x":0,"y":-10}},
  11. {"position":{"x":0,"y":10}, "mass":2,"strength":1e4,
  12. "balls":[
  13. {"colour":"blue","strength":1e3,
  14. "balls":[
  15. {"colour":"red", "strength":1e4}
  16. ]
  17. }
  18. ]
  19. }
  20. ]
  21. },
  22. {"colour":"#123456","position":{"x":250,"y":70},"mass":1,"radius":20,"strength":1e5,
  23. "balls":[
  24. {"colour":"red","position":{"x":-10,"y":40},"velocity":{"x":100,"y":10},"mass":1,"radius":10},
  25. {"colour":"red","position":{"x":60,"y":0}},
  26. {"colour":"red","position":{"x":70,"y":-10}}
  27. ]
  28. }
  29. ]

  JSON数据的读取:


  
  1. QJsonObject loadConfig() {
  2. // load json from config file
  3. QFile conf_file(config_path);
  4. conf_file.open(QIODevice::ReadOnly | QIODevice::Text);
  5. QString content = conf_file.readAll();
  6. conf_file.close();
  7. QJsonObject config = QJsonDocument::fromJson(content.toUtf8()).object();
  8. return config;
  9. }

  
  1. // for each of our balls, construct them
  2. QJsonArray ballData = m_conf->value("balls").toArray();
  3. if(!stageFlag)
  4. {
  5. for (const auto& item : ballData)
  6. {
  7. QJsonObject t = item.toObject();
  8. m_builder->addBall(t);
  9. }
  10. }else CreatBall(ballData);

  
  1. void GameDirector::CreatBall(QJsonArray ballData) //球的创建
  2. {
  3. for (const auto& item : ballData)
  4. {
  5. // PosIndex ++;
  6. m_BallPos_int[p_balltype] = PosIndex; //同一级别中,上一次所记录的最大值
  7. QJsonObject t = item.toObject();
  8. QJsonArray t_Arry =t.value("balls").toArray();
  9. if(!t_Arry.count()) { m_builder->addBall(t);}
  10. else
  11. {
  12. m_BallPos_int[p_balltype] = PosIndex;
  13. m_builder->addBall(t);
  14. p_balltype ++;
  15. PosIndex =m_BallPos_int[p_balltype];
  16. CreatBall(t_Arry); //递归寻找每一级的球
  17. }
  18. PosIndex ++;
  19. BallNum ++;
  20. m_BallPos_int[p_balltype] = PosIndex;
  21. }
  22. if(p_balltype>0) p_balltype --; else ; //每一次退出时,等级减一,回到上一级别
  23. PosIndex = m_BallPos_int[p_balltype]; //得到上一级上一次最后一个球的位置
  24. }

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

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

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


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

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

容器:


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

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

口袋吃球函数:


  
  1. bool PocketEatingBall(QVector2D pos,double ballrad)
  2. {
  3. bool status;
  4. QVector<CompontPocket *> Buf_m_pockets = p_CompositePockets.getvetor();
  5. for (auto it =Buf_m_pockets.begin();it!= Buf_m_pockets.end();it++)
  6. {
  7. CompontPocket * Buf_Compont = *it;
  8. pocket a_pocket = Buf_Compont->Output();
  9. 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()));
  10. double rad2 = ballrad*ballrad;
  11. double value = Log2-rad2;
  12. if(value<0) {status = 0;}
  13. else { status = 1; break;}
  14. }
  15. return status;
  16. }

球类元素删除函数:


  
  1. void Game::ballsDelItem()
  2. {
  3. int i =0,a=0;
  4. if(j == 0) return;
  5. for(a = 0;a<j;++a)
  6. {
  7. for (std::vector<Ball *>::iterator it = m_balls->begin();it!=m_balls->end();)
  8. {
  9. Ball * DelBall = *it;
  10. if(i== buf[a])
  11. {
  12. DelBallbuf[a][0] = DelBall->getBalltype();
  13. DelBallbuf[a][1] = DelBall->getBalltypeIndex();
  14. it = m_balls->erase(it);
  15. // qDebug()<<"Del A Ball"<<m_balls->size()<<DelBallbuf[a][0]<<DelBallbuf[a][1];
  16. }
  17. else ++it;
  18. ++i;
  19. }
  20. }
  21. NextBallsShow(j);
  22. }

子球显示函数:


  
  1. void Game::NextBallsShow(int Num)
  2. {
  3. for(int a = 0;a<Num;++a)
  4. {
  5. for (QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it)
  6. {
  7. if(it.key()->balltype==DelBallbuf[a][0]&&it.key()->ballindex==DelBallbuf[a][1])
  8. {
  9. // qDebug()<<"No instert"<<m_balls->size();
  10. m_balls->push_back(it.value());
  11. // qDebug()<<"Instert "<<m_balls->size();
  12. }
  13. }
  14. }
  15. }

动能功能:


  
  1. bool Game::EnergyCalculation(Ball* ballA) //碰撞能量计算
  2. {
  3. bool status; //状态值
  4. float ballMass = ballA->getMass();
  5. float ballStrength = ballA->getstrength();
  6. float ballRadius = ballA->getRadius();
  7. QVector2D preCollisionVelocity = ballA->getVelocity()+=changeVec;
  8. QVector2D deltaV = changeVec;
  9. float energyOfCollision = ballMass*deltaV.lengthSquared(); //能量计算公式
  10. // qDebug()<<energyOfCollision << deltaV.lengthSquared()<<changeVec.x()<<changeVec.y();
  11. int balltype = ballA->getBalltype();
  12. int ballIndex = ballA->getBalltypeIndex();
  13. int numComponentBalls = 0;
  14. for(QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it) //得到当前球里边包含子球的个数
  15. {
  16. Ball * componentBall = *it;
  17. if(componentBall->getBalltypeIndex() == balltype && componentBall->getBalltypeIndex() == ballIndex)
  18. {
  19. numComponentBalls++;
  20. }
  21. }
  22. if(ballStrength<energyOfCollision) //承受质量小于碰撞能量
  23. {
  24. float energyPerBall = energyOfCollision/numComponentBalls; //子球的能量
  25. deltaV.normalize();
  26. QVector2D pointOfCollision((-deltaV)*ballRadius);
  27. //for each component ball
  28. for(QMap<ParentStr *,Ball *>::iterator it = childball.begin();it!=childball.end();++it)
  29. {
  30. Ball * componentBall = *it;
  31. if(componentBall->getBalltypeIndex() == balltype && componentBall->getBalltypeIndex() == ballIndex)
  32. {
  33. QVector2D buf = componentBall->getPosition()-pointOfCollision;
  34. buf.normalize();
  35. QVector2D componentBallVelocity = preCollisionVelocity + sqrt(energyPerBall/componentBall->getMass())*buf; //子球速度的计算
  36. componentBall->changeVelocity(componentBallVelocity); //子球速度的设置
  37. }
  38. }
  39. status = 1;
  40. }else status =0;
  41. return status;
  42. }

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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