GameView.cpp
来源:互联网 发布:孔有德 知乎 编辑:程序博客网 时间:2024/05/01 16:29
#include "stdafx.h"
#include "GameClient.h"
#include "GameView.h"
#include "Site_i.h"
#include "../transStruct.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
template<bool b> struct Complier_Error;
template<> struct Complier_Error<true> { };
class CPaintDCWithUR : public CDC
{
public:
CPaintDCWithUR(CWnd * pWnd): m_paintDC(pWnd){
CreateCompatibleDC(&m_paintDC);
CRect rect = GetUpdateRect();
m_memBmp.CreateCompatibleBitmap(this, rect.Width(), rect.Height());
m_pOldMemBmp = SelectObject(&m_memBmp);
m_pt = SetWindowOrg(rect.TopLeft());
}
~CPaintDCWithUR() {};
void flush() {
SetWindowOrg(m_pt);
CRect rect = GetUpdateRect();
m_paintDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(),
this, 0, 0, SRCCOPY);
SelectObject(m_pOldMemBmp);
};
CRect GetUpdateRect() {
return m_paintDC.m_ps.rcPaint;
}
private:
CPaintDC m_paintDC;
CBitmap m_memBmp, *m_pOldMemBmp;
CPoint m_pt;
};
EXPOSE_GAMECREATOR_SUPPORTSITE(CGameWnd, GAME_ID, MAKEWORD(0, 1), CLIENT_AREA_CX, CLIENT_AREA_CY)
// 头像存放在res.dll
static HMODULE hResDll =::LoadLibrary("res.dll");
// IGameConst
STDMETHODIMP CGameWnd::GetEstimateTime(DWORD * pMillesecond)
{
// TODO:填写游戏运行的估计时间。如果少于这个时间的1/5,那么系统不会计分。
// 将这个值设为0可以关闭这个检查
return E_NOTIMPL;
}
STDMETHODIMP CGameWnd::GetPlayerNum(IGameConst::PLAYERNUMBER * pNumber)
{
ASSERT(pNumber);
// TODO:填写玩家的数量
pNumber->nMax =4;
pNumber->nStart =4;
pNumber->nMaintenance =4;
return S_OK;
}
STDMETHODIMP CGameWnd::GetGameIdentifier(char * lpszIdentifier)
{
lstrcpyn(lpszIdentifier, "Ee", 255);
return S_OK;
}
STDMETHODIMP CGameWnd::CanQuickStart()
{
//Complier_Error<false> you_must_read_Readme_first_to_emmit_this_error_CanQuickStart;
return S_OK;
}
STDMETHODIMP CGameWnd::CanSaveGame()
{
return E_NOTIMPL;
}
STDMETHODIMP CGameWnd::GetVersion(IGameConst::VERSION_STRUCT * pVS)
{
ASSERT(pVS);
// TODO: 填写内部版本号
// 这个号码用于避免玩家使用老的客户端来连入新的服务器
pVS->curVersion = MAKEWORD(0,1);
pVS->maxVersion = MAKEWORD(0,1);
pVS->minVersion = MAKEWORD(0,1);
return S_OK;
}
// IGameRender
STDMETHODIMP CGameWnd::OnInitialize()
{
// 在此处获得自己(myself)的指针,用户列表的指针
m_pSite->GetMyself(&m_pMyself);
m_pSite->GetPlayerList(&m_pList);
return S_OK;
}
STDMETHODIMP CGameWnd::OnWaitingStart()
{
//调整界面,使界面至少包含开始按钮
//不用判断自己是不是旁观,(旁观不会收到该事件)
// TODO:自己处理这个事件
return S_OK;
}
STDMETHODIMP CGameWnd::OnGameStart()
{
//在这个里往往不用做什么,大部分工作在OnSceneChanged里完成
return S_OK;
}
STDMETHODIMP CGameWnd::OnGameEnd(void * scorebuf)
{
Game_SCORE * scoreArray =(Game_SCORE *)scorebuf;
// TODO:你必须自己处理这个事件
//游戏结束一轮之后会调用这个事件,scorebuf是一个数组,按照位置顺序存储所有人的分数
//下面的scoreArray[0]是坐在0号位置的玩家成绩
//每个单元都是一个Ee_SCORE的结构。
int i;
CString strTemp;
CString strResult;
IGamePlayer * pPlayer = NULL;
//显示出最后的结果
for(i=0;i<MAX_PLAYER_COUNT;++i){
if(m_CPlayer[i].m_bRunaway)
strcat(m_CPlayer[i].m_PlayerName,"[逃跑者]");
if( NONE_CARD != m_CPlayer[i].m_nCardNum ){
strTemp.Format("玩家牌数:%d 玩家换牌次数:%d 玩家得分:%d 玩家姓名:%s",
m_CPlayer[i].m_nCardNum,m_CPlayer[i].m_nChangeNum,scoreArray[i].lScore,m_CPlayer[i].m_PlayerName);
strResult += strTemp+'/n';
}
}
MessageBox(strResult);
//对玩家数据进行清理
//千万千万不要清理完了哦。。。要不然,自己的数据都没有了,咋个显示哇
for(i=0;i<MAX_PLAYER_COUNT;++i){
m_pList->GetUser(i, &pPlayer);
if( pPlayer->IsValidUser() ) //判断下,是否是有效玩家(逃跑了的不算有效玩家了)
m_CPlayer[i].ReStart();
else
m_CPlayer[i].clear(); //无效玩家直接把数据清空
}
m_nGameState = GS_RESTART;
return S_OK;
}
STDMETHODIMP CGameWnd::OnSceneChanged(SCENE * pScene)
{
Game_SCENE * p_APP_Scene =(Game_SCENE *)pScene;
// TODO:你必须自己处理这个事件
//最重要的函数啦,在写之前先考虑清楚数据怎么组织
//一般而言,现场改变意味着开始新一轮
// 如果轮到自己动作,准备接受用户操作,同时启动时钟
//轮到自己动作,必须确认自己是player
//服务器的数据通常是可以信赖的,不需要太多的校验
//调用解析函数进行解析
ParseScene(p_APP_Scene,pScene);
Invalidate(FALSE);
return S_OK;
}
//解析收到的场景包的函数
void CGameWnd::ParseScene(Game_SCENE * p_APP_Scene,SCENE * pScene)
{
BYTE *pBuf = (BYTE*)pScene->lpdata; //一个BYTE指针,指向lpdata的
pBuf += sizeof(struct Game_SCENE);
if( !p_APP_Scene->bALLOutCard ){
//(一)当游戏进行中,只能看到自己的牌的时候的解析方式
int num = p_APP_Scene->nScentNum; //循环解析次数
for(int i=0;i<num;++i){
//取得座位号
int nSitNum = *(int*)pBuf;
pBuf += sizeof(int);
//出牌与否
m_CPlayer[nSitNum].m_bOutCard = *(BOOL*)pBuf;
pBuf += sizeof(BOOL);
//换牌次数
m_CPlayer[nSitNum].m_nChangeNum = *(int*)pBuf;
pBuf += sizeof(int);
//如果这个是发送给自己的,则取得玩家自己的牌数
IGamePlayer* pPlayer = NULL;
m_pList->GetUser(nSitNum, &pPlayer);
if( pPlayer == m_pMyself ){
m_CPlayer[nSitNum].m_nCardNum = *(int*)pBuf;
pBuf += sizeof(int);
}
}
}
else{
//(二)当所有玩家都出牌后的解析方式
int num = p_APP_Scene->nScentNum; //循环解析次数
for(int i=0;i<num;++i){
//取得座位号
int nSitNum = *(int*)pBuf;
pBuf += sizeof(int);
//换牌次数
m_CPlayer[nSitNum].m_nChangeNum = *(int*)pBuf;
pBuf += sizeof(int);
//玩家牌数
m_CPlayer[nSitNum].m_nCardNum = *(int*)pBuf;
pBuf += sizeof(int);
//玩家是否逃跑
m_CPlayer[nSitNum].m_bRunaway = *(BOOL*)pBuf;
pBuf += sizeof(BOOL);
//玩家得分
m_CPlayer[nSitNum].m_nScore = *(int*)pBuf;
pBuf += sizeof(int);
}
}
}
STDMETHODIMP CGameWnd::Stop(char * lpszReason)
{
// lpszReason是服务器端的dismiss函数中传过来的参数
// 通常我们只是显示一下而已
// TODO:你可以在这里使用自己的方式处理这个事件
::MessageBox(m_hWnd, lpszReason, "客户端->Stop", MB_OK);
return S_OK;
}
STDMETHODIMP CGameWnd::OnPlayerStateChanged(int nIdx, int state, IGamePlayer * pPlayer)
{
// 每当用户的状态有改变时,包括举手、断线都会触发这个事件。
// 一般而言,我们应该重画界面以反映更新
// TODO:你可以在这里使用自己的方式处理这个事件
switch(state)
{
case sSit: //玩家刚坐下
m_nGameState = GS_START;
break;
case sReady: //有玩家举手。
m_CPlayer[nIdx].m_nPlayerState = sReady;
break;
case sPlaying:
m_CPlayer[nIdx].m_nPlayerState = sPlaying;
m_nGameState = GS_PLAYING;
//此时,游戏开始了。这个时候,需要给所有玩家先预先分配一个假的牌数
//用于表明这个玩家有牌,方便其后在OnUserExit中判断逃跑
//程序会先给所有玩家(包括)自己分配假牌,然后从场景包中,获得真牌.
m_CPlayer[nIdx].m_nCardNum = FALSE_CARD;
break;
default:
break;
}
Invalidate(FALSE);
return S_OK;
}
STDMETHODIMP CGameWnd::OnGameOperation(int opID, LPOPDATA oData,int nsize)
{
if (SUCCEEDED(m_quickStartelper.OnGameOperation(opID, oData))) {
return S_OK;
}
//从服务器发来的命令,这些命令的效果不会被累计到OnSceneChanged中
if (opID==GAMENOTIFY_CLIENT_SHOULD_CLOSE) {
// 客户端应当退出
GN_CLIENT_SHOULD_CLOSE * pInfo =(GN_CLIENT_SHOULD_CLOSE *)oData;
if (pInfo) {
MessageBox(pInfo->msg, "ddd");
}
g_pFrame->Quit();
return S_OK;
}
// TODO:加入你自己的处理代码
return S_OK;
}
STDMETHODIMP CGameWnd::RequestConfirmQuit()
{
//请求用户确认要退出(该事件是由用户试图关闭窗口引起的,这可以给用户一个反悔的机会)
// 通常是一个对话框,返回S_OK将关闭程序,E_FAIL继续游戏
return S_OK;
}
STDMETHODIMP CGameWnd::Clear()
{
//务必清除所有数据,包括用户信息
//该函数在自己离开椅子是被调用,此时程序中记录的所有信息都无效了
//第一次进入时不会调用该函数。首次的数据准备应该在其他地方完成
SAFE_RELEASE(m_pList);
SAFE_RELEASE(m_pSite);
SAFE_RELEASE(m_pMyself);
int i = 0;
//清理掉双缓冲使用的内存兼容DC和设备兼容位图
m_CGameMapping.clear();
//按钮类的清理
for(i=0;i<BTN_COUNT;++i)
m_CGameButton[i].clear();
//玩家数据的清理
for(i=0;i<MAX_PLAYER_COUNT;++i)
m_CPlayer[i].clear();
return S_OK;
}
STDMETHODIMP CGameWnd::EnterSinglePlayerMode()
{
//不是所有游戏都需要写这一块的
MessageBox("单机版哦~~~一般人我还不告诉他哦~~~");
return E_NOTIMPL;
}
void CGameWnd::OnButtonSetup()
{
//打开设置对话框
}
/////////////////////////////////////////////////////////////////////////////
// CGameWnd
CGameWnd::CGameWnd() : m_dwRef(0)
{
m_pSite =NULL;
m_pList =NULL;
m_pLink =NULL;
m_pMyself =NULL;
// TODO:初始化变量
}
CGameWnd::~CGameWnd()
{
//使用SAFE_RELEASE宏是一个好习惯,定义在utility.h
SAFE_RELEASE(m_pList);
SAFE_RELEASE(m_pSite);
SAFE_RELEASE(m_pMyself);
//此处要注意啊!最好不要在这个地方进行资源的释放,因为程序框架本身的原因
//客户端退出的时候,不会调用到这个析构函数的
//同样,自己添加的类的析构函数也是不会被调用到的。
//所以,资源的释放最好放在clear()接口中进行!
//然后,在OnDestroy()中调用clear()。
//切忌,切忌!
}
BEGIN_MESSAGE_MAP(CGameWnd, CWnd)
//{{AFX_MSG_MAP(CGameWnd)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_RECALCLAYOUT, OnReCalcLayout)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CGameWnd message handlers
BOOL CGameWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
HDC hDC=NULL;
hDC = ::GetDC(m_hWnd);
if(!hDC){
::MessageBox(NULL,"在OnCreate中,初始化hDC失败","Error",0);
return -1;
}
//初始化双缓冲使用的内存兼容DC和设备兼容位图
if( !m_CGameMapping.InitDBB(hDC) ){
::MessageBox(NULL,"InitDBB失败","Error",0);
return -1;
}
if( !::ReleaseDC(m_hWnd,hDC) ){
::MessageBox(NULL,"在OnCreate中,释放hDC失败","Error",0);
return -1;
}
//玩家状态初始化
m_nGameState = GS_START;
m_nMySitNum = INVALID_SIT_NUM;
//初始化按钮
//按钮坐标
RECT rc_start = {10 ,500,60 ,530};
RECT rc_Over = {110,500,160,530};
RECT rc_change= {410,500,460,530};
RECT rc_out = {510,500,560,530};
m_CGameButton[START_BTN].InitButton("开始",rc_start);
m_CGameButton[QUIT_BTN].InitButton("退出",rc_Over);
m_CGameButton[CHANGE_CARD_BTN].InitButton("换牌",rc_change);
m_CGameButton[OUT_CARD_BTN].InitButton("出牌",rc_out);
return 0;
}
void CGameWnd::OnPaint()
{
//CPaintDCWithUR dc(this); // use this device context for painting
// TODO:把所有的绘图集中到此处,注意只需要绘制dc.GetUpdateRect()指定的区域
// 一般而言,我们绘制的顺序如下
// 背景
// 牌、或者棋盘
// 用户头像
// 其他用户信息
//dc.flush(); // this is the last sentence in this function
// Do not call CWnd::OnPaint() for painting messages
HDC hDC = NULL;
PAINTSTRUCT ps;
hDC = ::BeginPaint(m_hWnd,&ps);
if(!hDC)
::MessageBox(NULL,"在OnPaint中,初始化hDC失败","Error",0);
//贴图
m_CGameMapping.SelectObjectDBB();
m_CGameMapping.DrawGame(m_CPlayer);
//按钮的绘制
//1 根据m_nGameState对按钮的显示与否进行设置
ChangeBTNEnable(m_nGameState);
//2 贴出按钮
for(int i=0;i<BTN_COUNT;++i)
m_CGameButton[i].DrawButton(m_CGameMapping.GetHdcForDBB());
//贴出缓冲区中的图
m_CGameMapping.DrawDBB(hDC);
if( !::EndPaint(m_hWnd,&ps) )
::MessageBox(NULL,"在OnPaint中,释放hDC失败","Error",0);
}
BOOL CGameWnd::OnEraseBkgnd(CDC* pDC)
{
// 为避免闪动,最好屏蔽这个消息
return TRUE;
}
void CGameWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
char lpButtonText[1024];
//开始
if( m_CGameButton[START_BTN].ClickButton(lpButtonText,point,nFlags) ){ //首先判断是否有点击按钮
if( 0 == strcmp("开始",lpButtonText) ){ //接着,在判断是点击的哪个按钮
m_nGameState = GS_SHOWHAND;
m_quickStartelper.OnReady(m_pMyself->get_ID());
}
}
//退出
else if( m_CGameButton[QUIT_BTN].ClickButton(lpButtonText,point,nFlags) ){
if( 0 == strcmp("退出",lpButtonText) )
g_pFrame->Quit();
}
//换牌。需要先判断下,是否已经到了换牌的最大次数了
else if( m_CGameButton[CHANGE_CARD_BTN].ClickButton(lpButtonText,point,nFlags) ){
if( 0 == strcmp("换牌",lpButtonText) ){
//判断换牌的次数是否已经到了最大值
if( MAX_CHANGE_CARD_NUM == m_CPlayer[m_nMySitNum].m_nChangeNum )
MessageBox("SORRY,换牌次数已经到了最大值了哦");
else
m_pSite->SendMsg(CLICK_CHANGECARD_BTN, NULL, 0);
}
}
//出牌
else if( m_CGameButton[OUT_CARD_BTN].ClickButton(lpButtonText,point,nFlags) ){
if( 0 == strcmp("出牌",lpButtonText) ){
m_nGameState = GS_OUTCARD;
m_pSite->SendMsg(CLICK_OUTCARD_BTN, NULL, 0);
}
}
}
void CGameWnd::OnMouseMove(UINT nFlags, CPoint point)
{
for (int i=0;i<BTN_COUNT;++i)
m_CGameButton[i].TouchButton(point,nFlags);
Invalidate(TRUE);
}
LRESULT CGameWnd::OnReCalcLayout(WPARAM reserved, LPARAM lParam)
{
ASSERT(lParam);
RECALCLAYOUTINFO * pInfo =(RECALCLAYOUTINFO *)lParam;
//当框架大小发生改变时通过该消息通知子窗体改变大小,如果子窗不处理该消息,则框架自动把它放置到默认的位置
//游戏窗口如果支持变大小应该处理该消息,收到该消息时请根据rcControlWnd的位置来调整自己的位置
//或者更野蛮的做法,强行改变掉rcControlWnd的大小
//如果是竖直分割条,框架总是把分割条放置在控制窗口的左边,如果是水平分割条,框架总是把滚动条放在控制窗口的上边
// TODO:必须处理这个消息以便让Frame能够调整游戏窗口的大小
return 1;
}
void CGameWnd::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
// TODO: 必须在这个消息中安排界面上的元素
}
void CGameWnd::OnDestroy()
{
//数据清理
Clear();
}
//根据游戏状态,改变按钮的显示(起作用)与否
void CGameWnd::ChangeBTNEnable(int nGameState)
{
//把所有按钮的显示与否都放入这个函数中,方便将来的修改和维护
if( (GS_START == m_nGameState) || ( GS_RESTART == m_nGameState ) ){
m_CGameButton[START_BTN].SetButtonEnable(TRUE);
m_CGameButton[QUIT_BTN].SetButtonEnable(TRUE);
m_CGameButton[CHANGE_CARD_BTN].SetButtonEnable(FALSE);
m_CGameButton[OUT_CARD_BTN].SetButtonEnable(FALSE);
}
else if( (GS_SHOWHAND == m_nGameState) || (GS_OUTCARD == m_nGameState ) ){
m_CGameButton[START_BTN].SetButtonEnable(FALSE);
m_CGameButton[QUIT_BTN].SetButtonEnable(FALSE);
m_CGameButton[CHANGE_CARD_BTN].SetButtonEnable(FALSE);
m_CGameButton[OUT_CARD_BTN].SetButtonEnable(FALSE);
}
else if( GS_PLAYING == m_nGameState ){
m_CGameButton[START_BTN].SetButtonEnable(FALSE);
m_CGameButton[QUIT_BTN].SetButtonEnable(FALSE);
m_CGameButton[CHANGE_CARD_BTN].SetButtonEnable(TRUE);
m_CGameButton[OUT_CARD_BTN].SetButtonEnable(TRUE);
}
}
//IPlayerIOEvent
STDMETHODIMP CGameWnd::OnUserEnter(int nIdx, IGamePlayer * pGamePlayer)
{
m_CPlayer[nIdx].clear(); //玩家进来时,先对数据清理下吧。这样可以避免很多无谓的错误。
// 获得触发这个事件的玩家姓名
pGamePlayer->get_UserName(m_CPlayer[nIdx].m_PlayerName);
//看是否是自己坐下触发的,是的话,增加个标示的
if( pGamePlayer == m_pMyself ){
m_nMySitNum = nIdx; //此处把自己做的座位号号码存储起来,方便将来使用滴。
strcat(m_CPlayer[nIdx].m_PlayerName,"[自己]");
}
// 获得触发这个事件的玩家的总分
long lTotalScore[5] = {0};
pGamePlayer->get_ScoreBuf(lTotalScore);
m_CPlayer[nIdx].m_nTotalScore = lTotalScore[0];
// 获得触发事件的玩家头像
int nFaceID = pGamePlayer->get_FaceID();
HBITMAP hFaceBmp =::LoadBitmap(hResDll, MAKEINTRESOURCE(IDR_FACE_FIRST+nFaceID));
if (!hFaceBmp)
{
//不能确切的知道该资源是否能加载
// 如果失败,用第一张图代替之
hFaceBmp =::LoadBitmap(hResDll, MAKEINTRESOURCE(IDR_FACE_FIRST));
}
ASSERT(hFaceBmp);
m_CPlayer[nIdx].m_hFaceBmp = hFaceBmp;
return S_OK;
}
STDMETHODIMP CGameWnd::OnUserExit(int nIdx, IGamePlayer * pGamePlayer)
{
//此处,区分下玩家是否是因为逃跑退出,如果是,则不清理
//该玩家的数据,等OnGameEnd使用完后,由OnGameEnd来清理
//判断玩家是否逃跑,最好使用下面的方式。
//再备注:此处pGamePlayer->IsValidUser()所得,一定是FALSE!
//判断是否是NONE_CARD,如果不是,就表明这个玩家手中是有牌的。
//那就可以肯定,这个玩家是逃跑者.
//就不清理它的数据。只清理非逃跑者的数据
if( NONE_CARD == m_CPlayer[nIdx].m_nCardNum )
m_CPlayer[nIdx].clear();
return E_NOTIMPL;
}
//断线
STDMETHODIMP CGameWnd::OnUserOffline(int nIdx, IGamePlayer * pGamePlayer)
{
return E_NOTIMPL;
}
//断线后又回来
STDMETHODIMP CGameWnd::OnUserReplay(int nIdx, IGamePlayer * pGamePlayer)
{
return E_NOTIMPL;
}
- GameView.cpp
- GameView.h
- 显示界面类--GameView
- 2 游戏组件 GameView
- 【Unity】监听GameView分辨率变化
- Unity 编辑器中获取GameView的分辨率
- cpp
- CPP
- CPP
- cpp
- cpp
- CPP
- CPP
- 4、cocos2d-Lua的demo--游戏逻辑GameView
- android小游戏制作心得(四)——GameView的实现
- Unity3D 大型游戏 最后一站 源码 部分重点 GameView-BaseWindow(16)
- Unity3D 大型游戏 最后一站 源码 部分重点 GameView-LoginWindow(17)
- ClientSockThread.cpp&&ClientUrlThread.cpp
- putty自动登录
- 应用Crypto++计算文件的MD5校验和
- 积累的VC编程小技巧之标题栏和菜单
- 处理【System.Data.SqlClient.SqlError:媒体集有2个媒体簇,但只提供了1个。必须提供所有成员。】
- snmpwalk与snmpget的区别
- GameView.cpp
- 今天天气不错
- 非常有用的WSDL,有天气预报,股市,交通等等...
- GameView.h
- 游戏工作笔记
- libmemcached安装
- 理解oracle数据库中的public用户的作用
- 用VC++6.0实现PC机与单片机之间串行通信的方法
- 回来了