这是款联机版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 WIN32APP
if (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
//move
vec3 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 game
dynamic_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);
//绘制反射指导线1
if (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;
}
//changeturn
if (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
{
//罚一杆 返还黑8
otherPlayer->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))
{
//加对方罚球可能是2
if (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 3
case 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°/s
m_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;
}
}
}