这是款联机版3D桌球游戏,带有自动挂机算法,支持单机npc和联网对战。添加了瞄准辅助线功能。统一的架构,可以方便的嵌入rpg中。选手模型可以使用mmorpg中的玩家的模型。

选手动画暂时只做了一个站姿击球和一个坐姿击球,当选手和桌面发生碰撞时会从站姿切换到坐姿,中间自动添加过渡动画。手部动画配合拉杆力度使用计算预制关键帧的方法。鼠标控制拉杆角度选手移动位置时,脚步动作使用上下分身控制不同的动作。

碰撞计算是离散的,没有使用物理引擎,没有考虑到转动惯量的影响,这里是一个可以改进的地方(目前默认击球点在球的正中部位)。

AI算法,寻找最佳目标球(白色球-目标球-某个袋 - 距离之和越小越好, 夹角越小越好,行进路线上不能有干扰球)。

球的碰撞及运动只同步了初始状态, 后续的运动本机模拟,正常情况下简单的实现了同步。碰撞结果也暂时本机模拟,有待host处理。 同步了玩家瞄准动画。

源代码:

游戏类

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/Billiards/MiniGameBilliards.h
//  @Brief:     MiniGameBilliards
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#ifndef  __MiniGameBilliards__H__
#define  __MiniGameBilliards__H__#include "Billiards/BilliardsBall.h"
#include "Rpg/MiniGame.h"
#include "Render/Texture.h"#define BallCount 16    //球个数
#define pocketRadius 3  //球袋半径//球ID
enum BallID
{WhiteBall = 0,yellow_solidBall,blue_solidBall,red_solidBall,purple_solidBall,orange_solidBall,green_solidBall,brown_solidBall,BlackBall = 8,yellow_stripeBall,blue_stripeBall,red_stripeBall,purple_stripeBall,orange_stripeBall,green_stripeBall,brown_stripeBall,
};//队
enum TeamSide
{TeamSide_Null=-1,TeamSide_Stripe = 0,TeamSide_Solid,
};//为了避免计算误差引起的不同步,otherplayer击球引发的ball rolling计算在本机不处理碰撞,碰撞信息由otherplayer发过来,否则小的角度误差导致不同步。
enum MiniBilliardsCmd
{CMD_PrepareShoot,//准备击球 同步所有球的位置CMD_StickMove,   //同步当前玩家杆子角度CMD_ShootStick  ,//击球//CMD_BallMove    ,//球碰撞及运动 自己模拟即可无需同步CMD_BallIn      ,//进球  同步结果避免差异或外挂    CMD_GameOver    ,CMD_Restart     ,
};
const char* BilliardsCmdToString(int enumeration);class BilliardsPlayer;
class BilliardsPlayerRole;
class SoundChannel;class MiniGameBilliards:public MiniGame
{
public:MiniGameBilliards();virtual~MiniGameBilliards();virtual bool Start();virtual bool Stop();virtual bool Render();virtual void RenderUI();virtual bool Update();virtual bool Free();virtual bool KeepResource(bool once,int& circle,String& nextTip);//三种类型结构virtual MiniPlayer*  CreatePlayer();virtual MiniPlayer*  CreateRobot ();virtual MiniPlayer*  CreateRole  ();void DeflectBalls(BilliardsBall& left, BilliardsBall&right);void PocketInBall(BilliardsBall& ball,int pocket);//某方进球数int  InPocketNum(TeamSide side);//桌台边框碰撞点vec3 GetBoundPosFromInner(const vec3& pos,const vec3& dir);vec3 GetBoundPosFromOut(const vec3& pos,const vec3& dir);//处理游戏网络命令包virtual int  ProcessPacketCmd(PacketBase* packet);//virtual const char* CmdToString(const char* stream,int len);//发送推杆消息bool SendShootStick(float shootAngleRad,float shootPower);//发送瞄准动画消息bool SendStickMove(float shootAngleRad,float stickDistance);bool ShootStick(float shootAngleRad,float shootPower);bool SendPrepareShooting();//获得球所属的 实心还是花bool IsTeamSide(BallID id,TeamSide side);BilliardsPlayer* GetTurnPlayer(); enum GameState{PrepareShooting,BallRolling,Shooting,};GameState m_GameState;BilliardsPlayerRole* m_myRolePlayer;BilliardsBall    m_balls[BallCount];vec3  m_worldDeskMin;vec3  m_worldDeskMax;vec3  m_worldDeskCen;vec3  WhiteStartPos;vec3  PocketPos[6];int   m_curTurn;float m_prepareTime;           //准备时间float m_simulateTime;private:RendSys::MovieClip* m_movieStick[2];RendSys::MovieClip* m_movieBalls[BallCount];RendSys::MovieClip* m_movieBestPocket;TexturePtr m_ballIcons[BallCount];TexturePtr m_powerBar;TexturePtr m_lazer;//黑8是否进洞bool       m_blackInPacket; float      m_oldFov;
};extern MiniGameBilliards* G_BilliardsGame;#endif//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/Billiards/MiniGameBilliards.cpp
//  @Brief:     MiniGameBilliards
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#include "General/Pch.h"
#include "General/General.h"
#include "General/StringUtil.h"
#include "General/Timer.h"
#include "General/Window.h"
#include "Gui/GuiMgr.h"
#include "Gui/RpgGuis.h"
#include "Billiards/MiBilliardsBall_PlayGui.h"
#include "Billiards/BilliardsPlayer.h"
#include "Input/InputMgr.h"
#include "Billiards/MiniGameBilliards.h"
#include "Math/MathLibAdvance.h"
#include "Render/Camera.h"
#include "Render/Font.h"
#include "Render/MC_Misc.h"
#include "Render/RendDriver.h"
#include "Render/Shader.h"
#include "Packet/PacketMiniGame.h"
#include "Rpg/SyncGameInfo.h"
#include "Sound/ChannelSound.h"
#include "Sound/SoundManager.h"
#include "General/Pce.h"
#include "General/Option.h"class CameraCtrlerBilliards:public CameraCtrlerTarget
{
public:CameraCtrlerBilliards();virtual void  Update();float m_dir;float m_pitch;
};const char* BilliardsCmdToString(int enumeration)
{switch(enumeration){case CMD_PrepareShoot:return "CMD_PrepareShoot";     case CMD_ShootStick  :return "CMD_ShootStick  ";
//  case CMD_BallMove    :return "CMD_BallMove    ";      case CMD_BallIn      :return "CMD_BallIn      ";      case CMD_GameOver    :return "CMD_GameOver    ";      case CMD_Restart     :return "CMD_Restart     ";                         default             :return "CMD_unknow";}return "CMD_unknow";
}CameraCtrlerBilliards::CameraCtrlerBilliards()
{m_dir = 0;m_pitch = -40;
}void CameraCtrlerBilliards::Update()
{if (G_BilliardsGame==NULL){//游戏结束return;}//CheckBoderRot(-HALFPI/8,HALFPI/3);CheckWheelDis(40,150);//static float keyMoveSpeed = 100;static float mouseRotSpeed = 10;Camera* camera = G_Camera;float time = G_Timer->GetStepTime();//camera->SetRot(0,-40,0);//rotate//#ifdef WIN32APPif (G_Mouse->IsButtonPressed(MOUSE_RIGHT)){vec2 p = G_Mouse->GetMove();p *= -1;float dir = p.x*time*mouseRotSpeed;//camera->AddRot(dir,pitch,0);m_dir += dir;if (m_dir>180){m_dir = -180;}else if (m_dir<-180){m_dir = -180;}}else if (G_Mouse->IsButtonPressed(MOUSE_LEFT)){vec2 p = G_Mouse->GetMove();p *= -1;float dir = p.x*time*mouseRotSpeed;//camera->AddRot(dir,pitch,0);m_dir += dir;if (m_dir>180){m_dir = -180;}else if (m_dir<-180){m_dir = 180;}float pitch = p.y*time*mouseRotSpeed;m_pitch += pitch;if (m_pitch<-80){m_pitch = -80;}else if (m_pitch>-20){m_pitch = -20;}}G_Camera->SetEuler(m_dir,m_pitch,0);//#endif//movevec3 tarPos = G_BilliardsGame->m_worldDeskCen;//tarPos = m_balls[WhiteBall].m_pos;SetTarPos(tarPos);camera->SetEyePos(tarPos - camera->GetHeadDir()*m_distToTar);}MiniGameBilliards *G_BilliardsGame;MiniGameBilliards::MiniGameBilliards(): m_myRolePlayer(NULL)
{G_BilliardsGame = this;CmdEnumToString = BilliardsCmdToString;for (int i = 0; i < BallCount; i++){m_movieBalls[i] = NULL;}for (int i = 0; i < 2; i++){m_movieStick[i] = NULL;}}MiniGameBilliards::~MiniGameBilliards()
{G_BilliardsGame = NULL;
}bool MiniGameBilliards::Start()
{m_myRolePlayer = NULL;m_oldFov = G_Option->m_defaultFov;if(!MiniGame::Start())return false;vec3 localDeskMin;vec3 localDeskMax;if (m_movieScene == NULL){LoadConfig loader(LoadConfig::GenDonotReShrinkBound, true, true);m_movieScene = new RendSys::MovieClip;m_movieScene->LoadFromFile("data/minigame/billiards/desk.movie", &loader);m_movieScene->Advance();localDeskMin = m_movieScene->GetMovieClip("tag_min")->GetCollideSys().m_min;localDeskMax = m_movieScene->GetMovieClip("tag_max")->GetCollideSys().m_min;vec3 min = localDeskMin;vec3 max = localDeskMax;//坐标系转换后可能不对了if (localDeskMax.y < min.y){localDeskMax.y = min.y;}if (localDeskMin.y > max.y){localDeskMin.y = max.y;}if (localDeskMax.z < min.z){localDeskMax.z = min.z;}if (localDeskMin.z > max.z){localDeskMin.z = max.z;}Frame frame;frame.SetPos(m_startPos);m_movieScene->SetProgramFrame(&frame);m_movieScene->Advance();}for (int i = 0; i < 2; i++){if (m_movieStick[i] == NULL){char buf[128];sprintf(buf,"data/minigame/billiards/stick0%d.movie",i+1);LoadConfig loader(LoadConfig::GenDonotReShrinkBound, true, true);m_movieStick[i] = new RendSys::MovieClip;m_movieStick[i]->LoadFromFile(buf, &loader);m_movieStick[i]->SetFrustumSkipEnable(false, Recursive);m_movieStick[i]->Advance();}}m_movieBestPocket = m_movieScene->GetMovieClip("bestPocket");if (m_movieScene->IsLoadComplete() == false){m_gameState = MS_End;return false;}//for (int b = 0; b < BallCount; b++){m_balls[b].Reset();m_balls[b].m_id = (BallID)b;}m_blackInPacket = false;m_prepareTime = 0;m_balls[0].SetPos(vec3(0.0f, 0 , localDeskMin.z - localDeskMin.z / 3));//初始靠的太紧,碰撞不平衡m_balls[15].SetPos(vec3(7.0f, 0 , 27.0f));m_balls[1 ].SetPos(vec3(3.5f, 0 , 27.0f));m_balls[14].SetPos(vec3(0.0f, 0 , 27.0f));m_balls[13].SetPos(vec3(-3.5f, 0 , 27.0f));m_balls[2 ].SetPos(vec3(-7.0f, 0 , 27.0f));m_balls[3 ].SetPos(vec3(5.25, 0 , 23.5f));m_balls[12].SetPos(vec3(1.75, 0 , 23.5f));m_balls[4 ].SetPos(vec3(-1.75, 0 , 23.5f));m_balls[11].SetPos(vec3(-5.25, 0 , 23.5f));m_balls[10].SetPos(vec3(3.5f, 0 , 20.0f));m_balls[8].SetPos(vec3(0.0f, 0 , 20.0f));//black 在中间m_balls[5].SetPos(vec3(-3.5f, 0 , 20.0f));m_balls[6].SetPos(vec3(1.75, 0 , 16.5f));m_balls[7].SetPos(vec3(-1.75, 0 , 16.5f));m_balls[9].SetPos(vec3(0.0f, 0 , 13.0f));m_worldDeskMin = m_startPos + localDeskMin;m_worldDeskMax = m_startPos + localDeskMax;m_worldDeskCen = (m_worldDeskMin+m_worldDeskMax)*0.5f;m_simulateTime = 0;for (int b = 0; b < BallCount; b++){m_balls[b].m_pos += m_worldDeskCen;}WhiteStartPos = m_balls[0].GetPos();PocketPos[0] = vec3(m_worldDeskMin.x,  m_worldDeskCen.y, m_worldDeskMax.z);PocketPos[1] = vec3(m_worldDeskMax.x,  m_worldDeskCen.y, m_worldDeskMax.z);PocketPos[2] = vec3(m_worldDeskMin.x,  m_worldDeskCen.y, m_worldDeskCen.z);PocketPos[3] = vec3(m_worldDeskMax.x,  m_worldDeskCen.y, m_worldDeskCen.z);PocketPos[4] = vec3(m_worldDeskMin.x,  m_worldDeskCen.y, m_worldDeskMin.z);PocketPos[5] = vec3(m_worldDeskMax.x,  m_worldDeskCen.y, m_worldDeskMin.z);//==================^_^==================^_^==================^_^==================^_^for (int i = 0; i < m_allPlayerNum; i++){BilliardsPlayer* thePlayer = dynamic_cast<BilliardsPlayer*>(m_miniPlayer[i]);thePlayer->m_turn = i;Style* style = G_StyleMgr->GetStyle(thePlayer->GetPlayerInfo()->style);thePlayer->SetStyle(style);thePlayer->GetRenderCharacter()->SetScale(vec3(3.0f,3.0f,3.0f));thePlayer->GetRenderCharacter()->PlayAnim("billiards");//,"data/minigame/billiards");thePlayer->Start();}m_GameState = PrepareShooting;// First player in list starts the gamedynamic_cast<BilliardsPlayer*>(m_miniPlayer[0])->m_shootNum = 1;m_curTurn = 0;//设置摄像机G_Camera->PopCtrler();CameraCtrlerBilliards* ctrl = new CameraCtrlerBilliards;ctrl->SetTarPos(m_worldDeskCen);G_Camera->PushCtrler(ctrl);//片头摄像机PushIntroCamera();//进入miniplaygui,(选人、选关卡都已在房间里进行完毕)。if(GetStyle()) G_GuiMgr->PushGui(GetStyle()->playGUI.c_str(),GL_DIALOG);G_Option->m_defaultFov = 60;return true;}
MiniPlayer* MiniGameBilliards::CreatePlayer()
{return new BilliardsPlayer;
}MiniPlayer* MiniGameBilliards::CreateRobot()
{return new BilliardsPlayerRobot;
}MiniPlayer* MiniGameBilliards::CreateRole()
{m_myRolePlayer = new BilliardsPlayerRole;return m_myRolePlayer;
}
bool MiniGameBilliards::Stop()
{//G_Option->m_defaultFov = m_oldFov;G_Camera->PopCtrler();for (int i = 0; i < BallCount; i++){SafeDelete(m_movieBalls[i]);}for (int i = 0; i < 2; i++){SafeDelete(m_movieStick[i]);//SafeDelete(m_miniPlayer[i]);}G_GuiMgr->PopGui("MiBilliardsBall_PlayGui");{if (m_myRolePlayer && m_myRolePlayer->m_turn==m_curTurn){G_GuiMgr->GetGui<Rpg_ResultDialog>()->ShowResult(true);}else{G_GuiMgr->GetGui<Rpg_ResultDialog>()->ShowResult(false);}G_GuiMgr->PushGui("Rpg_ResultDialog",GL_DIALOGBOTTOM); }MiniGame::Stop();return true;
}bool MiniGameBilliards::Render()
{m_movieScene->RendClip();//绘制玩家for (int i = 0; i < m_allPlayerNum; i++){m_miniPlayer[i]->Render();}//绘制球  todo 调出高光效果 反射贴图 阴影贴图{mat4 mat;for (int p = 0; p < BallCount; p++){G_RendDriver->PushMatrix();//G_RendDriver->LoadIdentity(); //ogl 没有单独的world 会把modelview一起清除m_balls[p].m_rot.ToMatrix(mat);mat.SetTranslate(m_balls[p].m_pos);G_RendDriver->MultMatrix(mat);m_movieBalls[p]->Advance();m_movieBalls[p]->RendClip();G_RendDriver->PopMatrix();}}if (m_GameState == PrepareShooting||m_GameState == Shooting){//绘制球杆 todo billboard绘制更好看{G_RendDriver->PushMatrix();G_RendDriver->Translatef(m_balls[WhiteBall].m_pos.x, m_balls[WhiteBall].m_pos.y, m_balls[WhiteBall].m_pos.z);G_RendDriver->Rotatef(-GetTurnPlayer()->m_shootAngleRad * RAD2DEG + 90, 0.0f, 1.0f , 0.0f);G_RendDriver->Rotatef(-5.5f, 1.0f, 0.0f , 0.0f);//G_RendDriver->Rotatef(11.5f, 1.0f, 0.0f , 0.0f);G_RendDriver->Translatef(0.0f, 0.0f, GetTurnPlayer()->m_stickDistance*0.05f);m_movieStick[m_curTurn]->Advance();m_movieStick[m_curTurn]->RendClip();G_RendDriver->PopMatrix();}if(G_ShaderMgr)G_ShaderMgr->PushShader();//绘制指导线if (GetTurnPlayer()->m_useLazer == true&&( G_RendDriver->RendPassStepFlag==NormalPass|| G_RendDriver->RendPassStepFlag==PreRenderMrt)){G_RendDriver->Color4f(1.0f, 1.0f, 1.0f, 0.5f);m_lazer->Bind();vec3  tar = GetBoundPosFromInner(m_balls[WhiteBall].m_pos,vec3(-cos(GetTurnPlayer()->m_shootAngleRad),0,-sin(GetTurnPlayer()->m_shootAngleRad)));float nearDist = 99999;vec3  whitePos = m_balls[WhiteBall].m_pos;vec3  dir = tar-whitePos; dir.Normalize();vec3  refDir;BilliardsBall* tarBall = NULL;Sphere sphere;sphere.r = m_balls[WhiteBall].m_radius*2;vec3 res;for (int p = 0; p < BallCount; p++){if (p==WhiteBall){continue;}sphere.c = m_balls[p].m_pos;float t = 0;if (IntersectRaySphere(whitePos,dir,sphere,t,res) && t<nearDist){tarBall = &m_balls[p];nearDist = t;refDir = tarBall->m_pos - res;refDir.Normalize();tar = res;////tar += refDir*m_balls[WhiteBall].m_radius;}}float tiley = (whitePos - tar).Length()/2;vec2 tex[6];tex[0] = vec2(0    ,tiley);tex[1] = vec2(0.25f ,tiley);tex[2] = vec2(0    ,0);tex[3] = vec2(0    ,0);tex[4] = vec2(0.25f,tiley);tex[5] = vec2(0.25f,0);//变色if (GetTurnPlayer()!=m_myRolePlayer){for(int i=0;i<6;i++){tex[i].x += 0.25f;}}//绘制指导线vec3 pos[6];vec3 left = dir.Cross(vec3(0,1,0));left.Normalize();left *= m_balls[WhiteBall].m_radius*0.8f;vec3 lev(0,-0.1f,0);pos[0] = whitePos - left + lev;pos[1] = whitePos + left + lev;pos[2] = tar - left + lev;pos[3] = tar - left + lev;pos[4] = whitePos + left + lev;pos[5] = tar + left + lev;G_RendDriver->RendTrigon(2,pos, tex);//绘制反射指导线1if (tarBall){vec3 refTar = GetBoundPosFromInner(tar,refDir);float tiley = (tar- refTar).Length()/2;for(int i=0;i<6;i++){if (tex[i].y>0){tex[i].y = tiley;}}vec3 left = refDir.Cross(vec3(0,1,0));left.Normalize();left *= m_balls[WhiteBall].m_radius*0.8f;vec3 lev(0,-0.2f,0);pos[0] = tar - left + lev;pos[1] = tar + left + lev;pos[2] = refTar-left*0.5f + lev;pos[3] = refTar-left*0.5f + lev;pos[4] = tar + left + lev;pos[5] = refTar+left*0.5f + lev;G_RendDriver->RendTrigon(2,pos, tex);}//绘制反射指导线2//....//绘制碰撞点if (tarBall){float tiley = 1;tex[0] = vec2(0.5f  ,tiley);tex[1] = vec2(0.75f ,tiley);tex[2] = vec2(0.5f  ,0);tex[3] = vec2(0.5f  ,0);tex[4] = vec2(0.75f ,tiley);tex[5] = vec2(0.75f ,0);vec3 left = refDir.Cross(vec3(0,1,0));left.Normalize();left *= m_balls[WhiteBall].m_radius;vec3 front = refDir*m_balls[WhiteBall].m_radius;pos[0] = tar - left - front;pos[1] = tar + left - front;pos[2] = tar - left + front;pos[3] = tar - left + front;pos[4] = tar + left - front;pos[5] = tar + left + front;G_RendDriver->RendTrigon(2,pos, tex);}}if(G_ShaderMgr)G_ShaderMgr->PopShader();//提示ai最佳袋子BilliardsPlayerRobot* robot = dynamic_cast<BilliardsPlayerRobot*>(GetTurnPlayer());Frame frame;if (robot && robot->m_workingWithAI){frame.SetPos(PocketPos[robot->m_bestPocket] - m_startPos);}else{frame.SetPos(vec3());}m_movieBestPocket->SetProgramFrame(&frame);}return true;
}void MiniGameBilliards::RenderUI()
{MiniGame::RenderUI();///G_RendDriver->BeginUI();G_RendDriver->Color4f(1,1,1,1);G_RendDriver->SetRenderStateEnable(RS_BLEND,true);G_RendDriver->BlendFunc(Blend_Filter);//G_ShaderMgr->PushShader();char shotBuffer[20];sprintf(shotBuffer, "%d", GetTurnPlayer()->m_shootNum);G_RendDriver->Color3f(1.0f, 1.0f, 1.0f);G_FontMgr->TextAtPos(vec2(80, 15), "Player: ");G_FontMgr->TextAtPos(vec2(80, 45), "Shots: ");G_RendDriver->Color3f(0.8f, 0.8f, 0.8f);G_FontMgr->TextAtPos(vec2(160, 15), GetTurnPlayer()->GetPlayerInfo()->playerName);G_FontMgr->TextAtPos(vec2(160, 45), shotBuffer);// 绘制玩家头像 move to ui//力度条if (m_GameState == PrepareShooting||m_GameState == Shooting){RectF rect(130, 175, 180, 15);if (m_curTurn==1){rect = RectF(G_Window->m_iWidth-130-180, 175, 180, 15);}RectF rect2(rect.x+1, rect.y+1, rect.width-2, rect.height-2);G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D, false);G_RendDriver->Color3f(1.0f, 1.0f, 0.0f);G_RendDriver->DrawRect(rect);G_RendDriver->Color3f(0.0f, 0.0f, 0.5f);G_RendDriver->DrawRect(rect2);G_RendDriver->Color3f(1.0f, 1.0f, 1.0f);G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D, true);m_powerBar->Bind();G_RendDriver->DrawTextureRect(RectF(rect2.x, rect2.y, GetTurnPlayer()->m_shootPower*rect2.width/100, rect2.height), RectF(0, 0, GetTurnPlayer()->m_shootPower / 100.0f, 1));}//已经进洞的球.if(GetTurnPlayer() && GetTurnPlayer()->m_teamSide>=0){RectF rect(200+m_curTurn*300, 140, 32, 32);int startBall = (1-GetTurnPlayer()->m_teamSide)*8+1;for (int k = startBall; k < startBall+7; k++){if (m_balls[k].m_inPocket == true){G_RendDriver->Color3f(1.0f, 1.0f, 1.0f);m_ballIcons[k]->Bind();G_RendDriver->DrawTextureRect(rect);rect.x += 36.0f;}}}//G_ShaderMgr->PopShader();
}bool MiniGameBilliards::Update()
{if (m_movieScene == NULL){return false;}m_movieScene->Advance();switch(m_GameState){case BallRolling:{bool moving = true;m_simulateTime += G_Timer->GetStepTimeLimited(); //多个更新步模拟,避免单帧移动过大出现多次碰撞//减小模拟间隔 或使用连续性检测才能提高 robot的瞄准率float steptime = 0.002f;while(m_simulateTime>=steptime){//steptime = min(0.01f,frametime);//最后一个剩余时间累积到下一帧,使得各方碰撞结果尽量几乎一致(更新时间步一致)m_simulateTime -= steptime;//球和球碰撞for (int b = 0; b < (BallCount - 1); b++){for (int t =b+1; t < BallCount; t++){DeflectBalls(m_balls[b], m_balls[t]);}}//球和洞碰撞for (int b = 0; b < BallCount; b++){BilliardsBall& ball = m_balls[b];for (int p=0;p<6;p++){if(ball.m_inPocket==false){PocketInBall(m_balls[b], p);}   }}//球和桌面碰撞for (int b = 0; b < BallCount; b++){BilliardsBall& ball = m_balls[b];if(ball.m_inPocket==false){float minX = m_worldDeskMin.x;float minZ = m_worldDeskMin.z ;float maxX = m_worldDeskMax.x ;float maxZ = m_worldDeskMax.z ;float ballRadius = ball.m_radius;if (   ((ball.m_pos.x > maxX-ballRadius) && ball.m_speed.x >0 )|| ((ball.m_pos.x < minX+ballRadius) && ball.m_speed.x <0 )){//速度摩擦力取反ball.m_speed *= 0.8f;ball.m_speed.x *= -1;ball.m_acc.x *= -1;m_sound->PlaySound__("data/sound/ui_click.wav");}if (   ((ball.m_pos.z > maxZ-ballRadius)  && ball.m_speed.z >0 )|| ((ball.m_pos.z < minZ+ballRadius)  && ball.m_speed.z <0 )){//速度摩擦力取反ball.m_speed *= 0.8f;ball.m_speed.z *= -1;ball.m_acc.z *= -1;m_sound->PlaySound__("data/sound/ui_click.wav");}反射位置//if ((ball.m_pos.x > maxX-ballRadius) || (ball.m_pos.x < minX+ballRadius)//   ||(ball.m_pos.z > maxZ-ballRadius) || (ball.m_pos.z < minZ+ballRadius))//{// vec3 tar = GetBoundPosFromOut(ball.m_pos,ball.m_headDir*-1);// //回退半径//    tar -= ball.m_headDir*ballRadius;//    //穿透力度//    //float interDis = (ball.m_pos - tar).Length()*0.8f;// vec3 newDir = ball.m_speed;//  newDir.Normalize();//   //ball.m_pos = tar+newDir*interDis;// m_sound->PlaySound__("data/sound/billiards/shoot.wav");//}}}moving = false;for (int p = 0; p < BallCount; p++){if (m_balls[p].m_inPocket==false){if (m_balls[p].UpdateMoving(steptime)){moving = true;}}m_balls[p].m_collided = false;}if (moving == false){break;}}if (m_bHost== true//m_myRolePlayer&&//&&GetTurnPlayer()->m_turn == m_myRolePlayer->m_turn&& moving == false){m_GameState = PrepareShooting;//滚动结束返还球if (m_balls[WhiteBall].m_inPocket){m_balls[WhiteBall].SetPos(WhiteStartPos);m_balls[WhiteBall].m_speed = vec3();m_balls[WhiteBall].m_inPocket = false;}if (m_balls[BlackBall].m_inPocket && m_gameState != MS_End){vec3 pos = m_worldDeskCen;//pos.z += m_localDeskMin.z*0.666f;m_balls[BlackBall].SetPos(pos);m_balls[BlackBall].m_speed = vec3();m_balls[BlackBall].m_inPocket = false;}//changeturnif (GetTurnPlayer()->m_shootNum<=0){m_turnTime = 0;m_curTurn++;m_curTurn%=2;//换手后设置杆数if(GetTurnPlayer()->m_shootNum<=0){GetTurnPlayer()->m_shootNum = 1;}}SendPrepareShooting();}}break;case PrepareShooting:m_prepareTime += G_Timer->GetStepTimeLimited(); break;case Shooting:{//推杆动画}break;}m_turnTime += G_Timer->GetStepTimeLimited();for (int i = 0; i < m_allPlayerNum; i++){m_miniPlayer[i]->Update();}return true;
}bool MiniGameBilliards::Free()
{MiniGame::Free();return true;
}bool MiniGameBilliards::KeepResource(bool once, int &circle, String &nextTip)
{char *ballTexFile[] ={"data/minigame/billiards/ball_white.png","data/minigame/billiards/ball_yellow_solid.png","data/minigame/billiards/ball_blue_solid.png","data/minigame/billiards/ball_red_solid.png","data/minigame/billiards/ball_purple_solid.png","data/minigame/billiards/ball_orange_solid.png","data/minigame/billiards/ball_green_solid.png","data/minigame/billiards/ball_brown_solid.png","data/minigame/billiards/ball_black.png","data/minigame/billiards/ball_yellow_stripe.png","data/minigame/billiards/ball_blue_stripe.png","data/minigame/billiards/ball_red_stripe.png","data/minigame/billiards/ball_purple_stripe.png","data/minigame/billiards/ball_orange_stripe.png","data/minigame/billiards/ball_green_stripe.png","data/minigame/billiards/ball_brown_stripe.png",};for (int i = 0; i < BallCount; i++){if (!m_movieBalls[i]){m_movieBalls[i] = new MovieClip;m_movieBalls[i]->LoadFromFile("data/minigame/billiards/ball.movie");m_movieBalls[i]->GetMovieClip("ball")->ReAttachTexture(ballTexFile[i]);m_movieBalls[i]->SetFrustumSkipEnable(false, Recursive);}}char *ballIconFiles[] ={"data/minigame/billiards/mini_white.png","data/minigame/billiards/mini_yellow_solid.png","data/minigame/billiards/mini_blue_solid.png","data/minigame/billiards/mini_red_solid.png","data/minigame/billiards/mini_purple_solid.png","data/minigame/billiards/mini_orange_solid.png","data/minigame/billiards/mini_green_solid.png","data/minigame/billiards/mini_brown_solid.png","data/minigame/billiards/mini_black.png","data/minigame/billiards/mini_yellow_stripe.png","data/minigame/billiards/mini_blue_stripe.png","data/minigame/billiards/mini_red_stripe.png","data/minigame/billiards/mini_purple_stripe.png","data/minigame/billiards/mini_orange_stripe.png","data/minigame/billiards/mini_green_stripe.png","data/minigame/billiards/mini_brown_stripe.png",};for (int i = 0; i < BallCount; i++){G_TextureMgr->AddTexture(m_ballIcons[i], ballIconFiles[i]);}G_TextureMgr->AddTexture(m_powerBar, "data/minigame/billiards/powerBar.png");G_TextureMgr->AddTexture(m_lazer, "data/minigame/billiards/lazer.png");return true;
}void MiniGameBilliards::DeflectBalls(BilliardsBall &ballA, BilliardsBall &ballB)
{//减小模拟间隔 或使用连续性检测才能提高 robot的瞄准率if (ballA.m_inPocket || ballA.m_collided|| ballB.m_inPocket || ballB.m_collided) {return;}vec3  posDif    = ballA.m_pos - ballB.m_pos;float posDifLen = posDif.Length();if (posDifLen >= (ballA.m_radius+ballB.m_radius)) {if (ballA.m_collideBall == &ballB){ballA.m_collideBall = NULL;}if (ballB.m_collideBall == &ballA){ballB.m_collideBall = NULL;}//未接触return;} vec3  speedDif = ballA.m_speed - ballB.m_speed;float speedLen = speedDif.Length();if (speedLen<_EPSILON){return;}if (posDifLen <0.001f){posDif = vec3(0, 0, 0.001f);posDifLen = 0.001f;}//ballA.m_pos += posDif * ((ballA.m_radius - posDifLen * 0.5f) / posDifLen);//ballB.m_pos -= posDif * ((ballB.m_radius - posDifLen * 0.5f) / posDifLen);//接触  回退到刚接触的时间点后再碰撞处理 更精确 保证robot的瞄准率float time = ((ballA.m_radius+ballB.m_radius)-posDifLen)/speedLen;ballA.m_pos -= ballA.m_speed * time;ballB.m_pos -= ballB.m_speed * time;posDif = ballA.m_pos - ballB.m_pos;ballA.m_collided = true;ballB.m_collided = true;if (ballA.m_collideBall != &ballB){m_sound->PlaySound__("data/sound/ui_click.wav");}ballA.m_collideBall = &ballB;ballB.m_collideBall = &ballA;float impulse = 0.0f;float e       = 0.8f;//动量守恒impulse = ((-1) * (1.0f + e) * speedDif.Dot(posDif) ) / (posDif .Dot (posDif * (2.0f / ballA.m_mass)));ballA.m_speed += posDif*(impulse / ballA.m_mass);ballB.m_speed -= posDif*(impulse / ballB.m_mass);
}//进洞
void MiniGameBilliards::PocketInBall(BilliardsBall& ball, int pocket)
{if (ball.m_inPocket){return;}//vec3 dif = PocketPos[pocket]-ball.GetPos();dif.y = 0;if (dif.Length()<pocketRadius){ball.m_inPocket = true;}//if (ball.m_inPocket){BallID ballID = ball.m_id;ball.m_pos.y -= 5;ball.m_inPocket = true;//ball.m_rotAngleRad = 0;m_sound->PlaySound__("data/sound/billiards/sunk.wav");BilliardsPlayer* player = GetTurnPlayer();BilliardsPlayer* otherPlayer = dynamic_cast<BilliardsPlayer*>(m_miniPlayer[(m_curTurn + 1) % 2]);if ((ballID == WhiteBall)){//白球进洞返还if (m_blackInPacket == true){//黑8同时进洞otherPlayer->m_shootNum = 2;player->m_shootNum = 0;}}else if (ballID == BlackBall){//黑8球进洞int teamSide = player->m_teamSide;m_blackInPacket = true;if (teamSide != TeamSide_Null){//7球全进,游戏结束if (InPocketNum(player->m_teamSide) == 7){m_gameState = MS_End;//strcat(endGame, "Player wins");}else{//罚一杆 返还黑8otherPlayer->m_shootNum = 2;player->m_shootNum = 0;}}}else{if (player->m_teamSide == TeamSide_Null){//第一次进球,决定花式player     ->m_teamSide = IsTeamSide(ballID,TeamSide_Solid)?TeamSide_Solid:TeamSide_Stripe;otherPlayer->m_teamSide = IsTeamSide(ballID,TeamSide_Solid)?TeamSide_Stripe:TeamSide_Solid;}if (m_blackInPacket == true){//黑8同时进洞otherPlayer->m_shootNum = 2;player->m_shootNum = 0;}else{//自己花式进洞if (IsTeamSide(ballID,player->m_teamSide)){//加对方罚球可能是2if (player->m_shootNum<2){player->m_shootNum++;}}}}}
}int  MiniGameBilliards::InPocketNum(TeamSide side)
{int inPocketNum = 0;int startBall = (1-side)*8+1;for (int k = startBall; k < startBall+7; k++){if (m_balls[k].m_inPocket == true)inPocketNum++;}return inPocketNum;
}vec3 MiniGameBilliards::GetBoundPosFromInner(const vec3& pos,const vec3& dir)
{AABB aabb(m_worldDeskMin,m_worldDeskMax);vec3 newpos = pos+dir*1000;vec3 newdir = dir*-1;newdir.y = 0; //aabb.y 非常小float t;vec3 tar;IntersectRayAABB(newpos,newdir,aabb,t,tar);return tar;
}vec3 MiniGameBilliards::GetBoundPosFromOut(const vec3& pos,const vec3& dir)
{vec3 tar;AABB aabb(m_worldDeskMin,m_worldDeskMax);float t;IntersectRayAABB(pos,dir,aabb,t,tar);return tar;
}bool MiniGameBilliards::SendShootStick(float shootAngleRad,float shootPower)
{C2SPacketMiniGameCmd packet;packet.WriteHeader();packet.WriteValue(CMD_ShootStick);  packet.WriteValue(m_curTurn);packet.WriteValue(GetTurnPlayer()->m_shootNum);packet.WriteValue(shootAngleRad); packet.WriteValue(shootPower); G_MiniGame->SendPacketToOther(&packet);return true;
}bool MiniGameBilliards::SendStickMove( float shootAngleRad,float stickDistance )
{C2SPacketMiniGameCmd packet;packet.WriteHeader();packet.WriteValue(CMD_StickMove);  //packet.WriteValue(m_curTurn);packet.WriteValue(shootAngleRad); packet.WriteValue(stickDistance); G_MiniGame->SendPacketToOther(&packet);return true;
}bool MiniGameBilliards::SendPrepareShooting()
{m_prepareTime = 0;C2SPacketMiniGameCmd packet;packet.WriteHeader();packet.WriteValue(CMD_PrepareShoot); packet.WriteValue(m_curTurn); //可能进洞 不一定换手packet.WriteValue(GetTurnPlayer()->m_shootNum); for (int b=0;b<BallCount;b++){packet.WriteValue(m_balls[b].m_inPocket); packet.WriteValue(m_balls[b].m_pos); }G_MiniGame->SendPacketToOther(&packet);return true;
}bool MiniGameBilliards::ShootStick(float shootAngleRad,float shootPower)
{m_balls[WhiteBall].m_speed.x = shootPower * (cos(shootAngleRad)) * (-1.1f);m_balls[WhiteBall].m_speed.z = shootPower * (sin(shootAngleRad)) * (-1.1f);m_sound->PlaySound__("data/sound/billiards/shoot.wav");m_GameState = MiniGameBilliards::BallRolling;m_simulateTime = 0;m_blackInPacket = false;return true;
}int  MiniGameBilliards::ProcessPacketCmd(PacketBase* packet)
{int cmd;packet->ReadValue(cmd);switch(cmd){case CMD_PrepareShoot:m_prepareTime = 0;//不一定换手packet->ReadValue(m_curTurn);packet->ReadValue(GetTurnPlayer()->m_shootNum);m_turnTime = 0;for (int b=0;b<BallCount;b++){packet->ReadValue(m_balls[b].m_inPocket); packet->ReadValue(m_balls[b].m_pos); m_balls[b].m_speed = vec3(0,0,0);}m_GameState = PrepareShooting;break;case CMD_StickMove:packet->ReadValue(GetTurnPlayer()->m_stickDistance);packet->ReadValue(GetTurnPlayer()->m_shootAngleRad);break;  //case CMD_BallIn:     //进球 case CMD_ShootStick://击球{对手//TetrisPlayer* otherPlayer = GetOtherPlayer();float shootAngleRad;float shootPower;packet->ReadValue(m_curTurn);packet->ReadValue(GetTurnPlayer()->m_shootNum); packet->ReadValue(shootAngleRad); packet->ReadValue(shootPower);ShootStick(shootAngleRad, shootPower);}break;//case CMD_GameOver:// {//     int turn = 0;//        packet->ReadValue(turn);//       m_winnerTurn = turn;//     m_turnTime = 0;//      m_gameStatus = Resulting;//        char sound[256];//      if (m_winnerTurn == m_lordTurn)//     {//         sprintf(sound,"data/sound/poker/play_lord_win");//        }//     else//      {//         sprintf(sound,"data/sound/poker/play_farmer_win");//      }//     if (m_winnerTurn%2) ////            strcat(sound,"_femail.wav");//        else//          strcat(sound,".wav");//       m_players[m_winnerTurn]->m_sound->PlaySound__(sound);//   }// break;//case CMD_Restart:// Free();//   Start();//  break;}return 0;
}bool MiniGameBilliards::IsTeamSide(BallID ball,TeamSide side)
{if (ball == WhiteBall || ball == BlackBall){return false;}if (side==TeamSide_Null){return true;//一球未进 决定花式}if (ball > BlackBall && side==TeamSide_Stripe){return true;}else if (ball < BlackBall && side==TeamSide_Solid){return true;}return false;
}BilliardsPlayer* MiniGameBilliards::GetTurnPlayer()
{return dynamic_cast<BilliardsPlayer*>(m_miniPlayer[m_curTurn]);
}

玩家类:

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/Billiards/BilliardsPlayer.h
//  @Brief:     MiniGameBilliards
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#ifndef  __BilliardsPlayer__H__
#define  __BilliardsPlayer__H__#include "BilliardsBall.h"
#include "Rpg/MiniGame.h"
#include "Render/Texture.h"enum TeamSide;class BilliardsPlayer: public LogicCharacter,public MiniPlayer
{
public:BilliardsPlayer();virtual ~BilliardsPlayer();virtual bool Start();virtual void Update();virtual void Render();int      m_score;int      m_shootNum;TeamSide m_teamSide;int      m_turn;    //side 不等于turn    vec2     m_shootClickPos;float    m_shootPower;float    m_stickDistance;float    m_shootAngleRad;//指导线bool     m_useLazer;
};class BilliardsPlayerRobot:public BilliardsPlayer
{
public:virtual bool Start();virtual void Update();void   SetWorkingWithAI(bool working);//寻找最佳目标球(白球-目标球-某个袋 距离和越小越好 夹角越小越好)void   FindBestGoal();bool   m_workingWithAI; int    m_bestBall;int    m_bestPocket;float  m_bestPower;float  m_bestAngleRad;float  m_dstPower;float  m_dstAngleRad;double m_thinkTime;
};class BilliardsPlayerRole:public BilliardsPlayerRobot
{
public:virtual bool Start();virtual void Update();
};#endif//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/Billiards/BilliardsPlayer.cpp
//  @Brief:     MiniGameBilliards
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#include "General/Pch.h"
#include "General/General.h"
#include "General/StringUtil.h"
#include "General/Timer.h"
#include "Gui/GuiMgr.h"
#include "Input/InputMgr.h"
#include "Billiards/MiniGameBilliards.h"
#include "Billiards/MiBilliardsBall_PlayGui.h"
#include "Billiards/BilliardsPlayer.h"
#include "Render/Camera.h"
#include "Render/Font.h"
#include "Render/MC_Misc.h"
#include "Render/RendDriver.h"
#include "Rpg/SyncGameInfo.h"
#include "Sound/ChannelSound.h"
#include "General/Pce.h"
#include "Math/MathLibAdvance.h"//
BilliardsPlayer::BilliardsPlayer()
:m_useLazer(true)
{
}BilliardsPlayer::~BilliardsPlayer()
{
}bool BilliardsPlayer::Start()
{m_shootNum = 0;m_teamSide = TeamSide_Null;m_shootAngleRad = _PI + _PI / 2.0f;m_shootPower = 0;m_score = 0;m_stickDistance = 0;return false;
}void BilliardsPlayer::Update()
{if (G_BilliardsGame->m_curTurn == m_turn){switch(G_BilliardsGame->m_GameState){case MiniGameBilliards::BallRolling:{//PlayAnim("stand");}break;case MiniGameBilliards::PrepareShooting:{mat4 rot;rot.FromRotateY(-(m_shootAngleRad + HALFPI));m_heading = rot* vec3(0, 0, 1);//    m_stickDistance 逆向运动学手部动画m_pos = G_BilliardsGame->m_balls[WhiteBall].m_pos - m_heading *30;m_pos.y -= 15;LogicCharacter::Update();//todo AABB aabb(G_BilliardsGame->m_worldDeskMin,G_BilliardsGame->m_worldDeskMax);if(TestPointAABB(m_pos,aabb)){//PlayAnim("billiards_sit");//坐姿}else{//PlayAnim("billiards"); //站姿}//手部动画有待改进,可以加入逆向运动学解算或直接计算预制关键帧。 根据stickDistance设置播放头即可}break;}}
}void BilliardsPlayer::Render()
{if (G_BilliardsGame->m_curTurn == m_turn){LogicCharacter::Render();}
}bool BilliardsPlayerRobot::Start()
{BilliardsPlayer::Start();m_workingWithAI = true;m_bestBall = 0;m_bestPocket = 0;m_bestPower = 0;return false;
}void BilliardsPlayerRobot::Update()
{BilliardsPlayer::Update();if (G_BilliardsGame->m_curTurn == m_turn){switch(G_BilliardsGame->m_GameState){case MiniGameBilliards::BallRolling:{//球和球碰撞m_shootAngleRad = 0;m_shootPower = 0;m_bestPower = 0;m_bestAngleRad = 0;}break;#define MaxThinkTime 3case MiniGameBilliards::PrepareShooting://右键 开始拉杆if (G_BilliardsGame->m_prepareTime > MaxThinkTime){//抬起开始推杆//最终决定误差 0.5°float amp = 1;m_shootAngleRad = m_bestAngleRad + 0.5f*DEG2RAD *(RandRange(-amp,amp));m_shootPower    = m_bestPower    + 10 *(RandRange(-amp,amp));m_shootAngleRad = fmod(m_shootAngleRad+TWOPI,TWOPI); Clamp(m_shootPower, 0,100);G_BilliardsGame->m_GameState = MiniGameBilliards::Shooting;}else{//拉杆if (m_bestPower<_EPSILON){//寻找最佳目标球FindBestGoal();//vec3  packetPos = G_BilliardsGame->PocketPos[m_bestPocket];vec3  hitPos = G_BilliardsGame->m_balls[m_bestBall].m_pos;vec3  difPD    = hitPos - packetPos;difPD.Normalize();//碰撞时白球所在的位置hitPos += difPD*(G_BilliardsGame->m_balls[WhiteBall].m_radius+G_BilliardsGame->m_balls[m_bestBall].m_radius);vec3  whitePos = G_BilliardsGame->m_balls[WhiteBall].m_pos;vec3  difWD = hitPos - whitePos;difWD.Normalize();m_bestAngleRad = _PI + atan2(difWD.z, difWD.x);//最佳力量 距离越远 需要力度越大m_bestPower = (packetPos-whitePos).Length();//撞击越偏(拐角越大)需要力度越大float dot = abs(difPD.Dot(difWD));dot = Max(dot,0.2f);m_bestPower /= dot;//计算白球反弹位置 到达某一区域 对下次击球更有利//...//m_bestAngleRad = fmod(m_bestAngleRad+TWOPI,TWOPI); Clamp(m_shootPower, 0,100);m_thinkTime   = 999;}m_thinkTime+= G_Timer->GetStepTimeLimited();if (m_thinkTime>0.3f){m_thinkTime = 0;float amp = 1-G_BilliardsGame->m_prepareTime/MaxThinkTime;amp = amp * amp;m_dstAngleRad = m_bestAngleRad + 45*DEG2RAD *(RandRange(-amp,amp));m_dstPower    = m_bestPower    + 50 *(RandRange(-amp,amp));m_dstAngleRad = fmod(m_dstAngleRad+TWOPI,TWOPI); Clamp(m_dstPower, 0,100);}float difAngRad = m_dstAngleRad-m_shootAngleRad;bool  addRot    = (difAngRad>0&&difAngRad<_PI)||(difAngRad<0&&difAngRad<-_PI);m_shootAngleRad += (addRot ? 1:-1)*G_Timer->GetStepTimeLimited()*100*DEG2RAD; //旋转速度100°/sm_shootPower    += ((m_dstPower-m_shootPower)>0 ? 1:-1)*G_Timer->GetStepTimeLimited()*50;m_shootAngleRad = fmod(m_shootAngleRad+TWOPI,TWOPI); Clamp(m_shootPower, 0,100);m_stickDistance = m_shootPower;//同步瞄准if (G_Timer->GetCurrentFrame()%10==0){G_BilliardsGame->SendShootStick(m_shootAngleRad,m_shootPower);}}break;case MiniGameBilliards::Shooting:{//推杆动画if (m_stickDistance >= 3.0f){m_stickDistance -= m_stickDistance / 2.0f;}else{m_shootNum -= 1;G_BilliardsGame->ShootStick(m_shootAngleRad,m_shootPower);G_BilliardsGame->SendShootStick(m_shootAngleRad,m_shootPower);m_shootPower = 3.0f;}}break;}}
}//寻找最佳目标球(白球-目标球-某个袋 距离和越小越好 夹角越小越好)
void BilliardsPlayerRobot::FindBestGoal()
{//白球-目标球-某个袋 之间不能有干扰球//各球到白球的距离float ballRadX2 = G_BilliardsGame->m_balls[WhiteBall].m_radius*2.1f;vec3  whitePos = G_BilliardsGame->m_balls[WhiteBall].m_pos;float dis[BallCount];for (int p = 0; p < BallCount; p++){dis[p] = (G_BilliardsGame->m_balls[p].m_pos - whitePos).Length();}//简单寻找最近的球m_bestBall = BlackBall;//if (G_BilliardsGame->InPocketNum(m_teamSide)<6){float bestScore = 0;vec3  difPW;for (int p = 0; p < BallCount; p++){BilliardsBall& pBall = G_BilliardsGame->m_balls[p];if(    pBall.m_inPocket==false //未进&& G_BilliardsGame->IsTeamSide((BallID)p,m_teamSide)){//距离越近分越高float scoreWD = 1.0f/dis[p];bool visibleWD = true;//白球-目标球 之间无干扰球difPW = pBall.m_pos - whitePos;  for (int b = 0; b < BallCount; b++){if (  dis[b] < dis[p]//距离更近才可能干扰&& b!=WhiteBall&& b!=p){BilliardsBall& bBall = G_BilliardsGame->m_balls[b];float area = (bBall.m_pos - whitePos).Cross(difPW).Length();float h = area*2/dis[p];if(-ballRadX2<h && h<ballRadX2){visibleWD = false;break;}}               }if (visibleWD)//无遮挡时加分{scoreWD += 10;}//寻找最佳的洞float scoreDP = 0;//int bestPocket = 0;//for (int i=0;i<6;i++)//{//    bool visibleDP = true;//目标球-袋 之间无干扰球//}float score = scoreWD + scoreDP;if (score > bestScore){bestScore = score;m_bestBall = p;//m_bestPocket = bestPocket;}}}}//若没有不被干扰球 考虑二次碰撞击球 三次碰撞击球 判断难度(误差大小)//...//寻找最佳的洞m_bestPocket = 0;float maxDot = 0;vec3  pocketPos;vec3  hitPos = G_BilliardsGame->m_balls[m_bestBall].m_pos;vec3  dif = hitPos - whitePos;dif.Normalize();vec3  dif2;for (int i=0;i<6;i++){pocketPos = G_BilliardsGame->PocketPos[i];dif2 = pocketPos-hitPos;dif2.Normalize();float dot = dif.Dot(dif2);if (dot>maxDot){maxDot = dot;m_bestPocket = i;}}
}void BilliardsPlayerRobot::SetWorkingWithAI(bool working)
{m_workingWithAI = working;
}bool BilliardsPlayerRole::Start()
{BilliardsPlayerRobot::Start();m_workingWithAI = false;return false;
}void BilliardsPlayerRole::Update()
{if (m_workingWithAI==true){return BilliardsPlayerRobot::Update();}BilliardsPlayer::Update();if (G_BilliardsGame->m_curTurn == m_turn){switch(G_BilliardsGame->m_GameState){case MiniGameBilliards::BallRolling:{//球和球碰撞}break;case MiniGameBilliards::PrepareShooting:{vec3 head = G_Camera->GetHeadDir();m_shootAngleRad = _PI + atan2(head.z, head.x);//右键 开始拉杆if (G_Mouse->IsButtonDowning(MOUSE_RIGHT)){//G_BilliardsGame->m_GameState = MiniGameBilliards::Shooting;m_shootClickPos = G_Mouse->GetMousePos();}else if (G_Mouse->IsButtonUping(MOUSE_RIGHT)){//抬起开始推杆G_BilliardsGame->m_GameState = MiniGameBilliards::Shooting;}else if (G_Mouse->IsButtonPressed(MOUSE_RIGHT)){//拉杆m_shootPower = (G_Mouse->GetMousePos().y - m_shootClickPos.y) / 3;if (m_shootPower < 0){m_shootPower = 0;}else if (m_shootPower > 100){m_shootPower = 100;}m_stickDistance = m_shootPower;//同步瞄准if (G_Mouse->GetMove().LengthSq()>0){G_BilliardsGame->SendShootStick(m_shootAngleRad,m_shootPower);}}}break;case MiniGameBilliards::Shooting:{//推杆动画if (m_stickDistance >= 3.0f){m_stickDistance -= m_stickDistance / 2.0f;}else{m_shootNum -= 1;G_BilliardsGame->ShootStick(m_shootAngleRad,m_shootPower);G_BilliardsGame->SendShootStick(m_shootAngleRad,m_shootPower);m_shootPower = 3.0f;}}break;}}
}

联机带AI版3D桌球游戏源码相关推荐

  1. 3D飞镖游戏源码ios版

    一款ios 3D飞镖游戏源码,通过物理引擎和重力感应来控制飞镖向目标物体击中!游戏比较简单,可以学习一下3D游戏的基本开发. 源码下载: http://code.662p.com/view/6262. ...

  2. 基于html5制作3D拳击游戏源码下载

    今天给大家分享一款基于HTML5实现的3d拳王游戏源码.这款实例适用浏览器:360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗. 不支持IE8及以下浏览器. 在线预览 ...

  3. html5游戏开发 网页版-捕鱼达人游戏源码下载

    html5游戏开发 网页版-捕鱼达人游戏源码下载 来玩一把! 转载于:https://www.cnblogs.com/jsfoot/p/3215371.html

  4. html5 canvas简易版捕鱼达人游戏源码

    插件描述:html5利用canvas写的一个js版本的捕鱼,有积分统计,鱼可以全方位移动,炮会跟着鼠标移动,第一次打开需要鼠标移出背景图,再移入的时候就可以控制炮的转动,因为是用的mouseover触 ...

  5. 免费资源分享(八) 3D跑酷游戏源码分享

    免费资源分享(八) 3D跑酷游戏源码分享 分享链接见文章底部. 如果该文章侵犯到您的权益,请及时主动留言联系,我们将及时删除相关内容. (需要 指定场景.模型.音效.各类游戏源码等资源的,可以在评论区 ...

  6. unity5.X简易的3d跑酷游戏源码。支持安卓+IOS双端 C#语言开发。

    unity5.X简易的3d跑酷游戏源码.支持安卓+IOS双端 C#语言开发.拿来学习研究和二次开发都很不错. 完整源码下载 unity5.X简易的3d跑酷游戏源码.支持安卓+IOS双端C#语言开发-U ...

  7. Android版的疯狂猜图游戏源码完整版分享

    这个游戏源码是在安装教程网那么分享过来的,Android版的疯狂猜图游戏源码完整版分享,也是本人之前很早以前发的一款游戏源码的,大家如果想了解一下,可以看看吧,不说多了,上一个图先吧.   > ...

  8. 拼图java源码_拼图游戏Java版源代码JAVA游戏源码下载

    Java版的拼图游戏,玩家能够自己更换图片,只需你把它分成块,另外它是以成绩=1000-时间(秒)-移动步数*10来决意你是否输了,按F1键起头游戏,Y健预览图片. 拼图游戏Java版源代码 (1 f ...

  9. 合金弹头java视频_Java swing实现的仿植物大战僵尸版合金弹头游戏源码附带视频指导教程...

    <p> <span style="font-family:微软雅黑;font-size:16px;color:#555555;line-height:2;"> ...

  10. 【180622】带音效的VC++俄罗斯方块游戏源码

    今天再次发一个经典的俄罗斯方块游戏的源程序,以前发有很多了,有兴趣的自己在本站翻翻看.今天发的这个带有音效,而且代码里的注释也超多,对学习VC++的小游戏编程绝对有启发. 里面有不少技巧在编写其它小游 ...

最新文章

  1. 2010年十大投资机会(转载)
  2. Java构造和解析Json数据之org.json
  3. mysql-增删改(DML)
  4. Codeigniter 4.0-dev 版源码学习笔记之六——控制器
  5. black.lst 丢失或被破坏,怎么解决
  6. 阿诺德图像加密c语言,基于Arnold置乱的数字图像加密算法(二)
  7. 百度突然发公告:将停止这个服务!
  8. 关于解决miui10国际版刷入之后无法认证的问题
  9. vue 小写金额转换为大写金额
  10. oracle自增序列带字母,[原创]Oracle自增序列
  11. c语言判断字符串是否对称,c语言 判断字符串是否中心对称
  12. 三维空间点的直线方程拟合
  13. Java 设置系统参数和运行参数
  14. 看美国无线路由器品牌用户满意度排行榜
  15. AI行业强者愈强?Tesra超算网络助力中小AI开发企业!
  16. curl命令 – 文件传输工具
  17. Windows 2008 R2 SP1更新补丁报错解决建议
  18. 南京渣硕求职路(网易美团头条百度面经)+Java学习路线(拙见)
  19. 任务6 学生宿舍信息管理系统
  20. 反向代理服务器nginx-proxy-manager

热门文章

  1. oc引导windows蓝屏_蓝屏错误疑难解答
  2. matlab 怎么画时域图,matlab画e时域波形图
  3. 服务器双系统快捷键,mac双系统切换快捷键
  4. 阿里云服务器 ECS 数据盘与系统盘是什么?
  5. 常用的即时通讯软件有哪些
  6. 金融货币学笔记(米什金)第三章 什么是货币 带原书总结
  7. HTML页面基本结构浅谈
  8. 小程序毕业设计 基于微信考试小程序毕业设计开题报告功能参考
  9. 路由追踪测试软件,路由追踪命令是什么 使用路由追踪的技巧
  10. vue3+ts 全局挂载以及声明写法