视图: 画出你的游戏界面

前面,我们的文档对象中已经初始化了游戏板对象,接下来我们需要显示这些信息给用户了。

第一步是添加代码,来重新设置我们的窗口尺寸。缺省的窗口尺寸不是我们想要的,我们将重写OnInitialUpdate 方法来实现这一点。视图类继承了一个缺省的OnInitialUpdate方法,我们希望重写它来重定义我们窗口的尺寸。OnInitialUpdate方法在客户区被初始化更新的时候调用。首先我们来看一下如何添加该方法。

切换到类视图,选中CSameGameView,然后按Alt+Enter

点击重写图标,如下图找到OnInitialUpdate方法进行重写:

以下是修改后的视图类头文件,注意字体着重的部分是修改的地方。

#pragma once

class CSameGameView : public CView
{
protected: // create from serialization only
CSameGameView();
DECLARE_DYNCREATE(CSameGameView) // Attributes
public:
CSameGameDoc* GetDocument() const;
// Overrides
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected: // Implementation
public:

void ResizeWindow();

virtual ~CSameGameView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif // Generated message map functions
protected:
DECLARE_MESSAGE_MAP()
public:
virtual void OnInitialUpdate();
}; #ifndef _DEBUG // debug version in SameGameView.cpp
inline CSameGameDoc* CSameGameView::GetDocument() const
{ return reinterpret_cast<CSameGameDoc*>(m_pDocument); }
#endif

在增加ResizeWindow方法的同时,我们也需要增加描绘游戏界面方法到CSameGameView类中。视图类的头文件和源文件中已经包含了一个 OnDraw方法,这里正是我们放置描绘界面代码的地方。以下是视图类实现类cpp的全部代码,注意着重字体的地方是新增的内容。

#include "stdafx.h"
#include "SameGame.h" #include "SameGameDoc.h"
#include "SameGameView.h" #ifdef _DEBUG
#define new DEBUG_NEW
#endif // CSameGameView
IMPLEMENT_DYNCREATE(CSameGameView, CView)
BEGIN_MESSAGE_MAP(CSameGameView, CView)
END_MESSAGE_MAP() // CSameGameView construction/destruction
CSameGameView::CSameGameView()
{
} CSameGameView::~CSameGameView()
{
} BOOL CSameGameView::PreCreateWindow(CREATESTRUCT& cs)
{
return CView::PreCreateWindow(cs);
} // CSameGameView drawing

void CSameGameView::OnDraw(CDC* pDC) // MFC will comment out the argument name by default; uncomment it
{
// First get a pointer to the document
CSameGameDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(!pDoc)
return;
// Save the current state of the device context
int nDCSave = pDC->SaveDC();
// Get the client rectangle
CRect rcClient;
GetClientRect(&rcClient);
// Get the background color of the board
COLORREF clr = pDoc->GetBoardSpace(-1, -1);
// Draw the background first
pDC->FillSolidRect(&rcClient, clr);
// Create the brush for drawing
CBrush br;
br.CreateStockObject(HOLLOW_BRUSH);
CBrush* pbrOld = pDC->SelectObject(&br);
// Draw the squares
for(int row = 0; row < pDoc->GetRows(); row++)
{
for(int col = 0; col < pDoc->GetColumns(); col++)
{
// Get the color for this board space
clr = pDoc->GetBoardSpace(row, col);
// Calculate the size and position of this space
CRect rcBlock;
rcBlock.top = row * pDoc->GetHeight();
rcBlock.left = col * pDoc->GetWidth();
rcBlock.right = rcBlock.left + pDoc->GetWidth();
rcBlock.bottom = rcBlock.top + pDoc->GetHeight();
// Fill in the block with the correct color
pDC->FillSolidRect(&rcBlock, clr);
// Draw the block outline
pDC->Rectangle(&rcBlock);
}
}
// Restore the device context settings
pDC->RestoreDC(nDCSave);
br.DeleteObject();
}
// CSameGameView diagnostics
#ifdef _DEBUG
void CSameGameView::AssertValid() const
{
CView::AssertValid();
} void CSameGameView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
} // non-debug version is inline
CSameGameDoc* CSameGameView::GetDocument() const
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSameGameDoc)));
return (CSameGameDoc*)m_pDocument;
}
#endif //_DEBUG void CSameGameView::OnInitialUpdate()
{
CView::OnInitialUpdate();

// Resize the window
ResizeWindow();

}
void CSameGameView::ResizeWindow()
{
// First get a pointer to the document
CSameGameDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(!pDoc)
return;
// Get the size of the client area and the window
CRect rcClient, rcWindow;
GetClientRect(&rcClient);
GetParentFrame()->GetWindowRect(&rcWindow);
// Calculate the difference
int nWidthDiff = rcWindow.Width() - rcClient.Width();
int nHeightDiff = rcWindow.Height() - rcClient.Height();
// Change the window size based on the size of the game board
rcWindow.right = rcWindow.left +
pDoc->GetWidth() * pDoc->GetColumns() + nWidthDiff;
rcWindow.bottom = rcWindow.top +
pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
// The MoveWindow function resizes the frame window
GetParentFrame()->MoveWindow(&rcWindow);
}

描绘游戏板是非常简单的,就是一行一行一列一列的画出每一个彩色的砖块到屏幕上。

我们首先将客户区背景色填充成黑色,其中客户区填充颜色的取得通过呼叫GetBoardSpace(-1,-1) 方法;客户区大小的取得通过呼叫 GetClientRect方法,最后通过调用FillSolidRect方法实现填充任务。

//  Get the client rectangle
CRect rcClient;
GetClientRect(&rcClient);
// Get the background color of the board
COLORREF clr = pDoc->GetBoardSpace(-1, -1);
// Draw the background first
pDC->FillSolidRect(&rcClient, clr);

接下来,我们来画一个一个的小砖块,我们要画一个带颜色的矩形和一个黑色的边框,为了实现这点我们将画刷的类型设置成HOLLOW_BRUSH,这样当我们调用Rectangle()方法时不会用默认的白色背景填充我们的小方块。

嵌套的for循环的逻辑是非常简单的,就是一行一行,一列一列的在客户区描绘出小方块。通过文档类,我们可以获得每一个小砖块块随机的颜色,我们还可以得到小砖块的大小,进而计算出每个小方块应该描绘的位置。我们通过FillSolidRect() 方法来填充小砖块的颜色。通过 Rectangle() 方法来画小砖块的边框。

//  Draw the squares
for(int row = 0; row < pDoc->GetRows(); row++)
{
for(int col = 0; col < pDoc->GetColumns(); col++)
{
// Get the color for this board space
clr = pDoc->GetBoardSpace(row, col);
// Calculate the size and position of this space
CRect rcBlock;
rcBlock.top = row * pDoc->GetHeight();
rcBlock.left = col * pDoc->GetWidth();
rcBlock.right = rcBlock.left + pDoc->GetWidth();
rcBlock.bottom = rcBlock.top + pDoc->GetHeight();
// Fill in the block with the correct color
pDC->FillSolidRect(&rcBlock, clr);
// Draw the block outline
pDC->Rectangle(&rcBlock);
}
}

接下来,我们要根据我们描绘的小砖块的个数及大小,重新计算窗口的大小。

//  Get the size of the client area and the window
CRect rcClient, rcWindow;
GetClientRect(&rcClient);
GetParentFrame()->GetWindowRect(&rcWindow);

注意我们取得了2个窗口的尺寸,一个是框架窗口(包含客户区,菜单,工具栏,及状态栏),一个是客户区的大小。我们首先需要计算出框架窗口和客户区窗口的高度差,宽度差;然后再把这个高度差加上我们所有砖块的行高度得到我们最终的窗口的高度,宽度的计算同理。

//  Calculate the difference
int nWidthDiff = rcWindow.Width() - rcClient.Width();
int nHeightDiff = rcWindow.Height() - rcClient.Height();
// Change the window size based on the size of the game board
rcWindow.right = rcWindow.left +
pDoc->GetWidth() * pDoc->GetColumns() + nWidthDiff;
rcWindow.bottom = rcWindow.top +
pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
// The MoveWindow function resizes the frame window

最后我们调用父窗口也即框架窗口的MoveWindow方法重新设置窗口的大小。

GetParentFrame()->MoveWindow(&rcWindow);

最终,我们的程序看起来应该是这样的:

结论

在这篇文章里,我们首先回顾了MFC的基本知识和文档视图结构的基本概念。接下来,我们抽象了一个游戏板类型,其中包含了我们的游戏数据信息,并且构建了一个视图将游戏信息描绘到了界面中,下一章,我们将运用事件驱动编程方法,实现与用户的交互,比如鼠标点击事件,来实现一个可玩的游戏版本。

 


The Same Game": A Simple Game from Start to Finish3的更多相关文章

  1. PHP设计模式(一)简单工厂模式 (Simple Factory For PHP)

    最近天气变化无常,身为程序猿的寡人!~终究难耐天气的挑战,病倒了,果然,程序猿还需多保养自己的身体,有句话这么说:一生只有两件事能报复你:不够努力的辜负和过度消耗身体的后患.话不多说,开始吧. 一.什 ...

  2. Design Patterns Simplified - Part 3 (Simple Factory)【设计模式简述--第三部分(简单工厂)】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part3-factory/ Design ...

  3. WATERHAMMER: A COMPLEX PHENOMENON WITH A SIMPLE SOLUTION

    开启阅读模式 WATERHAMMER A COMPLEX PHENOMENON WITH A SIMPLE SOLUTION Waterhammer is an impact load that is ...

  4. BZOJ 3489: A simple rmq problem

    3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 600 MBSubmit: 1594  Solved: 520[Submit] ...

  5. Le lié à la légèreté semblait être et donc plus simple

    Il est toutefois vraiment à partir www.runmasterfr.com/free-40-flyknit-2015-hommes-c-1_58_59.html de ...

  6. ZOJ 3686 A Simple Tree Problem

    A Simple Tree Problem Time Limit: 3 Seconds      Memory Limit: 65536 KB Given a rooted tree, each no ...

  7. 设计模式之简单工厂模式Simple Factory(四创建型)

    工厂模式简介. 工厂模式专门负责将大量有共同接口的类实例化 工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类. 工厂模式有三种形态: 1.简单工厂模式Simple Factory ...

  8. HDU 5795 A Simple Nim 打表求SG函数的规律

    A Simple Nim Problem Description   Two players take turns picking candies from n heaps,the player wh ...

  9. 关于The C compiler "arm-none-eabi-gcc" is not able to compile a simple test program. 的错误自省...

    在 GCC ARM Embedded https://launchpad.net/gcc-arm-embedded/ 上面下载了个arm-none-eabi-gcc 用cmake 编译时 #指定C交叉 ...

随机推荐

  1. jQuery动态效果实例

    jQuery常见的动态效果: 隐藏/显示效果: 1.(1):隐藏,显示:通过 jQuery,使用 hide() 和 show() 方法可以用来隐藏和显示 HTML 元素. (2):隐藏/显示的速度: ...

  2. 08_使用TCP/IP Monitor监视SOAP协议

    [SOAP定义] SOAP   简单对象访问协议,基于http传输xml数据,soap协议体是xml格式.SOAP   是一种网络通信协议SOAP   即Simple Object Access Pr ...

  3. LA 3902 Network(树上最优化 贪心)

    Network Consider a tree network with n <tex2html_verbatim_mark>nodes where the internal nodes ...

  4. struts2初印象

    第一次写这么正式的文章,如果写的不好的地方,请指出. 今天玩了一下struts2,不过貌似是我被他玩了.简要笔记如下: 一.配置struts2(在eclipse Helios版本下) (1)先创建一个 ...

  5. 用Session实现验证码

    新建一个 ashx 一般处理程序 如: YZM.ashx继承接口 IRequiresSessionState //在一般处理程序里面继承 HttpContext context 为请求上下文,包含此次 ...

  6. centos 不支持mysql

    感觉是php-odbc必须装 yum -y install php php-fpm yum –y install php-mysql php-common php-mbstring php-gd ph ...

  7. Linux文件权限学习总结

    一.用户对文件或目录都有哪些权限? 四种:读.写.执行.没有权限 二.如何表示这四种权限? 如果用十进制数字表示,分别为:4.2.1.0:如果用字符表示,分别为:r.w.x.-.个人觉得,使用chmo ...

  8. C# 调用 MFC DLL

    创建项目 创建MFCDLL项目 MFC项目中这么声明 生成dll工程 可以看到库文件的生成目录,保存下来 创建测试用c#项目 我们创建一个按钮调用我们刚才的函数 这边这么调用MFC库的函数入口.这里并 ...

  9. const和define的使用区别

    在PHP中(PHP 4及以后),我们可以使用函数define()来定义常量,例如: <?php define('PI',3.14159);  //定义一个名为PI的常量 echo PI;     ...

  10. MVC5框架解析之Controller的创建

    在上一讲中我们介绍了MvcHandler,知道在Handler里面注入两个接口属性分别为IControllerFactory和IController的factory和controller.并且通过IO ...