精华内容
下载资源
问答
  • 可以看到不管是在工作中还是在学习中,都会接触到很多PDF格式的文件,尤其是当人们在工作中遇到的时候,一些PDF文件过大,会给阅读和保存带来很大的不便,这就需要将PDF页面进行拆分,那么如何拆分PDF页面呢?...

    随着时间的推移PDF文件的重要性已经逐渐的显示出来了,可以看到不管是在工作中还是在学习中,都会接触到很多PDF格式的文件,尤其是当人们在工作中遇到的时候,一些PDF文件过大,会给阅读和保存带来很大的不便,这就需要将PDF页面进行拆分,那么如何拆分PDF页面呢?其实很简单,下面就来为大家讲解两种不同的方法来实现PDF拆分页面,这个简单方法你一定没用过!
    如何拆分PDF页面?这个简单方法你一定没用过

    准备工作:
    电脑
    浏览器
    迅捷PDF编辑器
    操作方法:
    第一步:运行我们下载安装好的软件,然后进入到软件的功能页面,在首页点击“文件”然后将准备好的PDF文件添加进去。
    如何拆分PDF页面?这个简单方法你一定没用过
    第二步:然后点击软件上方的选项“文件”然后点击下面的“更多页面”之后选择“拆分页面”
    如何拆分PDF页面?这个简单方法你一定没用过
    第三步:选择好之后可选择拆分的方式,有两种拆分方式分别是水平拆分,垂直拆分,选择好之后对“页面范围”进行选择选择好之后点击“确定”即可。
    如何拆分PDF页面?这个简单方法你一定没用过

    为了让大家更加清楚的看到拆分页面之后的效果,下面来为大家展示一下拆分之后的效果图
    如何拆分PDF页面?这个简单方法你一定没用过
    当然除了这种方法之外呢还有一种比较简单的方法,下面就来给大家介绍吧,是不是觉得很厉害啊
    软件:迅捷PDF转换器
    软件介绍:这款功能很强大哦,不仅仅是能够转换各种办公文档,功能也不仅仅是局限于PDF文件转换哦,还能够进行PDF分割和PDF合并,是不是很神奇呢

    首先我们是打开迅捷PDF转换器这个软件,然后进去到它的功能页面点击“PDF的其他操作”点击之后可选择下面的子栏目“PDF分割”之后点击“添加文件”文件添加好之后点击“开始转换”等待一会看到转换的状态为百分之百的时候,就是已经转换完成啦。
    如何拆分PDF页面?这个简单方法你一定没用过

    看到这里相信大家已经知道PDF拆分页面怎么做是最简单的啦,这两款软件都是比较简单的操作,看大家喜欢哪一个就可以使用哪一个啦,这些软件的功能不仅仅是这些哦,相信你们下载之后会给你们带来不一样的惊喜哦。

    转载于:https://blog.51cto.com/14035602/2352420

    展开全文
  • function drawBoard(){ context.beginPath(); for (var x = 0; x ; x += grid){ context.moveTo(0.5 + x + p, p); context.lineTo(0.5 + x + p, bh + p); } for (var x = 0; x ; x += grid) { context.moveTo(p, ...

    我在带有底层网格的画布上绘制多边形

    我想现在将这个多边形分成多个多边形(基于网格)

    因此,我得到4个多边形的坐标,而不是1个多边形.

    有没有一个简单的解决方案,我没有想到?

    这是我的测试画布的代码(codepen)

    var bw = 200;

    var bh = 200;

    var p = 0;

    var cw = bw + (p*2) + 1;

    var ch = bh + (p*2) + 1;

    var grid = 50;

    var canvas = document.getElementById("canvas");

    var context = canvas.getContext("2d");

    function drawBoard(){

    context.beginPath();

    for (var x = 0; x <= bw; x += grid){

    context.moveTo(0.5 + x + p, p);

    context.lineTo(0.5 + x + p, bh + p);

    }

    for (var x = 0; x <= bh; x += grid) {

    context.moveTo(p, 0.5 + x + p);

    context.lineTo(bw + p, 0.5 + x + p);

    }

    context.lineWidth = 1;

    context.strokeStyle = "black";

    context.stroke();

    }

    drawBoard();

    // Polygon

    context.fillStyle = '#f00';

    context.beginPath();

    context.moveTo(0, 0);

    context.lineTo(100,50);

    context.lineTo(50, 100);

    context.lineTo(0, 90);

    context.closePath();

    context.fill();

    展开全文
  • ] ] 虽然这是一个三维数组, 我们把它拆分下就好理解了 相当于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1] 因为对象可以这样取值: var obj = { a: 10, ...

    1. 创建实例

    function Gobang () {

    this.over = false; // 是否结束

    this.player = true; // true:我 false:电脑

    this.allChesses = []; // 所有棋子

    this.existChesses = [] // 已经落下的棋子

    this.winsCount = 0; // 赢法总数

    this.wins = []; // 所有赢法统计

    this.myWins = []; //我的赢法统计

    this.computerWins = []; //电脑赢法统计

    }

    2. 初始化

    //初始化

    Gobang.prototype.init = function(opts) {

    // 生成canvas棋盘

    this.createCanvas(opts);

    //棋盘初始化

    this.boardInit();

    // 鼠标移动聚焦功能实现

    this.mouseMove();

    //算法初始化

    this.algorithmInit();

    //落子功能实现

    this.dorpChess();

    }

    3. 生成canvas棋盘

    //初始化

    //生成canvas

    Gobang.prototype.createCanvas = function(opts) {

    var opts = opts || {};

    if (opts.width && opts.width%30 !== 0) throw new RangeError(opts.width+'不是30的倍数');

    this.col = (opts.width && opts.width/30) || 15; // 棋盘列

    var oCanvas = document.createElement('canvas');

    oCanvas.width = oCanvas.height = opts.width || 450;

    this.canvas = oCanvas;

    document.querySelector(opts.container || 'body').appendChild(this.canvas);

    this.ctx = oCanvas.getContext('2d');

    }

    4. 初始化棋盘

    //棋盘初始化

    Gobang.prototype.boardInit = function(opts){

    this.drawBoard();

    }

    // 画棋盘

    Gobang.prototype.drawBoard = function(){

    this.ctx.strokeStyle = "#bfbfbf";

    for (var i = 0; i < this.col; i++) {

    this.ctx.moveTo(15+ 30*i, 15);

    this.ctx.lineTo(15+ 30*i, this.col*30-15);

    this.ctx.stroke();

    this.ctx.moveTo(15, 15+ 30*i);

    this.ctx.lineTo(this.col*30-15, 15+ 30*i);

    this.ctx.stroke();

    }

    }

    bVZqEr?w=450&h=448

    5. 画棋子

    // 画棋子

    Gobang.prototype.drawChess = function(x, y, player){

    var x = 15 + x * 30,

    y = 15 + y * 30;

    this.ctx.beginPath();

    this.ctx.arc(x, y, 13, 0, Math.PI*2);

    var grd = this.ctx.createRadialGradient(x + 2, y - 2, 13 , x + 2, y - 2, 0);

    if (player) { //我 == 黑棋

    grd.addColorStop(0, '#0a0a0a');

    grd.addColorStop(1, '#636766');

    }else{ //电脑 == 白棋

    grd.addColorStop(0, '#d1d1d1');

    grd.addColorStop(1, '#f9f9f9');

    }

    this.ctx.fillStyle = grd;

    this.ctx.fill()

    }

    bVZqEw?w=456&h=459

    6. 移动聚焦

    // 鼠标移动时触发聚焦效果, 需要前面的聚焦效果消失, 所有需要重绘canvas

    Gobang.prototype.mouseMove = function(){

    var that = this;

    this.canvas.addEventListener('mousemove', function (e) {

    that.ctx.clearRect(0, 0, that.col*30, that.col*30);

    var x = Math.floor((e.offsetX)/30),

    y = Math.floor((e.offsetY)/30);

    //重绘棋盘

    that.drawBoard();

    //移动聚焦效果

    that.focusChess(x, y);

    //重绘已经下好的棋子

    that.redrawedChess()

    });

    }

    //鼠标移动聚焦

    Gobang.prototype.focusChess = function(x, y){

    this.ctx.beginPath();

    this.ctx.fillStyle = '#E74343';

    this.ctx.arc(15 + x * 30, 15 + y * 30, 6, 0, Math.PI*2);

    this.ctx.fill();

    }

    //重绘当前下好的棋子

    Gobang.prototype.redrawedChess = function(x, y){

    for (var i = 0; i < this.existChesses.length; i++) {

    this.drawChess(this.existChesses[i].x, this.existChesses[i].y, this.existChesses[i].player);

    }

    }

    bVZqE1?w=183&h=168

    7. 算法初始化

    //算法初始化

    Gobang.prototype.algorithmInit = function(){

    //初始化棋盘的每个位置和赢法

    for (var x = 0; x < this.col; x++) {

    this.allChesses[x] = [];

    this.wins[x] = [];

    for (var y = 0; y < this.col; y++) {

    this.allChesses[x][y] = false;

    this.wins[x][y] = [];

    }

    }

    //获取所有赢法

    this.computedWins();

    // 初始化电脑和我每个赢法当前拥有的棋子数

    for (var i = 0; i < this.winsCount; i++) {

    this.myWins[i] = 0;

    this.computerWins[i] = 0;

    }

    }

    8. 获取所有赢法

    Gobang.prototype.computedWins = function(){

    /*

    直线赢法

    以15列为准

    */

    for (var x = 0; x < this.col; x++) { //纵向所有赢法

    for (var y = 0; y < this.col-4; y ++) {

    this.winsCount ++;

    /*

    如:

    1.组成的第一种赢法

    [0,0]

    [0,1]

    [0,2]

    [0,3]

    [0,4]

    2.组成的第二种赢法

    [0,1]

    [0,2]

    [0,3]

    [0,4]

    [0,5]

    以此类推一列最多也就11种赢法, 所有纵向x有15列 每列最多11种, 所有纵向总共15 * 11种

    */

    //以下for循环给每种赢法的位置信息储存起来

    for (var k = 0; k < 5; k ++) {

    this.wins[x][y+k][this.winsCount] = true;

    /*

    位置信息

    第一种赢法的时候:

    this.wins = [

    [

    [1:true],

    [1:true],

    [1:true],

    [1:true],

    [1:true]

    ],

    [

    ......

    ]

    ]

    虽然这是一个三维数组, 我们把它拆分下就好理解了

    相当于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1]

    因为对象可以这样取值:

    var obj = {

    a: 10,

    b: 'demo'

    }

    obj['a'] === obj.a

    所有也就相当于 this.wins[0][0].1, this.wins[0][8].1, this.wins[0][9].1, this.wins[0][10].1, this.wins[0][11].1

    虽然数组不能这么取值,可以这么理解

    所以 this.wins[0][0].1 就可以理解为 在 x=0, y=0, 上有第一种赢法

    this.wins[0][12].1 就可以理解为 在 x=0, y=1, 上有第一种赢法

    ......

    以上this.wins[0][0],this.wins[0][13]...可以看作是 this.wins[x][y]

    所以第一种赢法的坐标就是: [0,0] [0,1] [0,2] [0,3] [0,4]

    */

    }

    }

    }

    for (var y = 0; y < this.col; y++) { //横向所有赢法, 同纵向赢法一样,也是15 * 11种

    for (var x = 0; x < this.col-4; x ++) {

    this.winsCount ++;

    for (var k = 0; k < 5; k ++) {

    this.wins[x+k][y][this.winsCount] = true;

    }

    }

    }

    交叉赢法

    bVZqHJ?w=555&h=405

    /*

    交叉赢法

    */

    for (var x = 0; x < this.col-4; x++) { // 左 -> 右 开始的所有交叉赢法 总共11 * 11种

    for (var y = 0; y < this.col-4; y ++) {

    this.winsCount ++;

    /*

    如:

    1. [0,0]

    [1,1]

    [2,2]

    [3,3]

    [4,4]

    2. [0,1]

    [1,2]

    [2,3]

    [3,4]

    [4,5]

    3. [0,2]

    [1,3]

    [2,4]

    [3,5]

    [4,6]

    ...

    [1,0]

    [2,1]

    [3,2]

    [4,3]

    [5,5]

    相当于从左至右 一列列计算过去

    */

    for (var k = 0; k < 5; k ++) {

    this.wins[x+k][y+k][this.winsCount] = true;

    }

    }

    }

    for (var x = this.col-1; x >= 4; x --) { //右 -> 左 开始的所有交叉赢法 总共11 * 11种

    for (var y = 0; y < this.col-4; y ++) {

    this.winsCount ++;

    for (var k = 0; k < 5; k ++) {

    this.wins[x-k][y+k][this.winsCount] = true;

    }

    }

    }

    }

    9. 落子实现

    //落子实现

    Gobang.prototype.dorpChess = function(){

    var that = this;

    this.canvas.addEventListener('click', function(e) {

    // 判断是否结束

    if (that.over) return;

    var x = Math.floor((e.offsetX)/30),

    y = Math.floor((e.offsetY)/30);

    //判断该棋子是否已存在

    if (that.allChesses[x][y]) return;

    // 检查落子情况

    that.checkChess(x, y)

    if (!that.over) {

    that.player = false;

    that.computerDropChess();//计算机落子

    }

    })

    }

    //检查落子情况

    Gobang.prototype.checkChess = function(x, y){

    //画棋

    this.drawChess(x, y, this.player);

    //记录落下的棋子

    this.existChesses.push({

    x: x,

    y: y,

    player: this.player

    });

    //该位置棋子置为true,证明已经存在

    this.allChesses[x][y] = true;

    this.currWinChesses(x, y, this.player);

    }

    //判断当前坐标赢的方法各自拥有几粒棋子

    Gobang.prototype.currWinChesses = function(x, y, player){

    var currObj = player ? this.myWins : this.computerWins;

    var enemyObj = player ? this.computerWins : this.myWins;

    var currText = player ? '我' : '电脑';

    for (var i = 1; i <= this.winsCount; i++) {

    if (this.wins[x][y][i]) { //因为赢法统计是从1开始的 所以对应我的赢法需要减1

    currObj[i-1] ++; // 每个经过这个点的赢法都增加一个棋子;

    enemyObj[i-1] = 6; //这里我下好棋了,证明电脑不可能在这种赢法上取得胜利了, 置为6就永远不会到5

    if (currObj[i-1] === 5) { //当达到 5 的时候,证明我胜利了

    alert(currText+'赢了')

    this.over = true;

    }

    }

    }

    }

    9. 计算机落子实现

    // 计算机落子

    Gobang.prototype.computerDropChess = function(){

    var myScore = [], //玩家比分

    computerScore = [], // 电脑比分

    maxScore = 0; //最大比分

    //比分初始化

    var scoreInit = function(){

    for( var x = 0; x < this.col; x ++) {

    myScore[x] = [];

    computerScore[x] = [];

    for (var y = 0; y < this.col; y ++) {

    myScore[x][y] = 0;

    computerScore[x][y] = 0;

    }

    }

    }

    scoreInit.call(this);

    //电脑待会落子的坐标

    var x = 0, y = 0;

    // 基于我和电脑的每种赢法拥有的棋子来返回对应的分数

    function formatScore(o, n) {

    if (o < 6 && o > 0) {

    var n = 10;

    for (var i = 0; i < o; i++) {

    n *= 3;

    }

    return n

    }

    return 0

    }

    // 获取没有落子的棋盘区域

    function existChess(arr) {

    var existArr = [];

    for (var i = 0; i < arr.length; i++) {

    for (var j = 0; j < arr[i].length; j++) {

    if (!arr[i][j]) {

    existArr.push({x:i, y:j})

    }

    }

    }

    return existArr;

    }

    var exceptArr = existChess(this.allChesses);

    // 循环未落子区域,找出分数最大的位置

    for (var i = 0; i < exceptArr.length; i++) {

    var o = exceptArr[i];

    // 循环所有赢的方法

    for (var k = 0; k < this.winsCount; k++) {

    //判断每个坐标对应的赢法是否存在

    if (this.wins[o.x][o.y][k]) {

    // 计算每种赢法,拥有多少棋子,获取对应分数

    // 电脑起始分数需要高一些,因为现在是电脑落子, 优先权大

    myScore[o.x][o.y] += formatScore(this.myWins[k-1], 10);

    computerScore[o.x][o.y] += formatScore(this.computerWins[k-1], 11);

    }

    }

    //我的分数判断

    if (myScore[o.x][o.y] > maxScore) { //当我的分数大于最大分数时, 证明这个位置的是对我最有利的

    maxScore = myScore[o.x][o.y];

    x = o.x;

    y = o.y;

    }else if (myScore[o.x][o.y] === maxScore) { //当我的分数与最大分数一样时, 证明我在这两个位置下的效果一样, 所以我们应该去判断在这两个位置时,电脑方对应的分数

    if (computerScore[o.x][o.y] > computerScore[x][y]) {

    x = o.x;

    y = o.y;

    }

    }

    // 电脑分数判断, 因为是电脑落子, 所以优先权大

    if (computerScore[o.x][o.y] > maxScore) {

    maxScore = computerScore[o.x][o.y];

    x = o.x;

    y = o.y;

    }else if (computerScore[o.x][o.y] === maxScore) {

    if (myScore[o.x][o.y] > myScore[x][y]) {

    x = o.x;

    y = o.y;

    }

    }

    }

    this.checkChess(x, y)

    if (!this.over) {

    this.player = true;

    }

    }

    var gobang = new Gobang();

    gobang.init()

    展开全文
  • } } } 拆分成小部分讲解: /* 设定玩家1为开始玩家 */ m_cur_click = ClickPlayer::ClickPlayer_Player1; 设定玩家1为先手(ClickPlayer为点击玩家枚举,后面进行讲解) /* 初始化九个格子 m_ppPreces不为NULL则为...

    上篇进行讲解了游戏的规则、界面设计、游戏流程、······

    下面我们继续码代码吧,(#^.^#)

     

    在写代码前先说之前所漏掉的两个枚举,分别是:

    ① ClickPlayer (枚举点击玩家)

    /* 点击玩家枚举 */
    typedef enum _ClickPlayer {
    	ClickPlayer_Player1 = 0x00000001,	// 玩家1
    	ClickPlayer_Player2 = 0x00000002	// 玩家2
    }ClickPlayer;

    ② GameOverType (枚举游戏结束类型)

    /* 游戏结局 */
    typedef enum _GameOverType {
    	GameOverType_Tie     = 0x00000001,	// 平局
    	GameOverType_Player1 = 0x00000002,	// 玩家1
    	GameOverType_Player2 = 0x00000004	// 玩家2
    }GameOverType;

     

    下面开始继续代码的讲解:

    2)游戏初始化(TicTacToe_Init)

    ①TicTacToe_Init

    VOID TicTacToe_Init(HWND hWnd)
    {
    	/* 游戏初始化 */
    	g_game.Init(hWnd);
    	/* 创建兼容DC和兼容位图 */
    	Util::CreateDoubleBuffer(hWnd, g_mdc, g_bitmap);
    	::SelectObject(g_mdc, g_bitmap);
    }

    g_game.Init(hWnd),进行初始化游戏中的游戏对象

    Util::CreateDoubleBuffer进行创建双缓冲(Util在后面进行讲解,此处先为了解)

    ::SelectObject将兼容位图放入兼容DC中

     

    ②Game::Init(游戏初始化)

    void Game::Init(HWND hWnd)
    {
    	/* 游戏初始化 */
    	m_bIsGameOver = false;
    	m_hWnd = hWnd;
    	/* 初始化棋盘 */
    	m_board.Init(hWnd);
    }

    m_bIsGameOver = false,游戏设定为未结束

    m_hWnd = hWnd,保存窗口句柄为后面使用

    m_board.Init(hWnd),初始化棋盘

     

    ③Board::Init(棋盘初始化)

    void Board::Init(HWND hWnd)
    {
    	/* 设定玩家1为开始玩家*/
    	m_cur_click = ClickPlayer::ClickPlayer_Player1;
    	::GetClientRect(hWnd, &m_rect);
    
    	/* 初始化九个格子 m_ppPreces不为NULL则为上局棋子 */
    	if (m_ppPreces != NULL) {
    		delete m_ppPreces;
    		m_ppPreces = NULL;
    	}
    	m_ppPreces = new Prece*[9];
    
    	/* 两边余量 */
    	int x_margin = 20;
    	int y_margin = 20;
    
            /* 设定棋盘矩形大小 */
    	m_rect.left += x_margin;
    	m_rect.top += y_margin;
    	m_rect.right -= x_margin;
    	m_rect.bottom -= y_margin;
    
    	/* 棋盘宽高 */
    	int width = m_rect.right - m_rect.left;
    	int height = m_rect.bottom - m_rect.top;
    
            /* 棋盘左上角(x, y) 以及棋子的宽和高 */
    	int x_start = m_rect.left;
    	int y_start = m_rect.top;
    	int w_distance = width / 3;
    	int h_distance = height / 3;
    
    	for (int c = 0, col = 3; c < col; ++c)
    	{
    		for (int r = 0, row = 3; r < row; ++r)
    		{
                            /* 创建棋盘格子,并保存到棋盘中 */
    			m_ppPreces[c * 3 + r] = new Prece(x_start + (r * w_distance), y_start + (c * h_distance), w_distance, h_distance, c * 3 + r);
    		}
    	}
    }
    

    拆分成小部分讲解:

    /* 设定玩家1为开始玩家 */
    m_cur_click = ClickPlayer::ClickPlayer_Player1;

    设定玩家1为先手(ClickPlayer为点击玩家枚举,后面进行讲解)

    /* 初始化九个格子 m_ppPreces不为NULL则为上局棋子 */
    if (m_ppPreces != NULL) {
    	delete m_ppPreces;
    	m_ppPreces = NULL;
    }
    m_ppPreces = new Prece*[9];

    如果已经创建了9个棋子,则释放此9个棋子。(如果不为NULL则是上一局的棋子)

    然后分配9个格子空间进行保存格子

    /* 两边余量 */
    int x_margin = 20;
    int y_margin = 20;
    
    /* 设定棋盘矩形大小 */
    m_rect.left += x_margin;
    m_rect.top += y_margin;
    m_rect.right -= x_margin;
    m_rect.bottom -= y_margin;

    设置棋盘的矩形大小,也就是给窗口四边留有相应的余量

    /* 棋盘宽高 */
    int width = m_rect.right - m_rect.left;
    int height = m_rect.bottom - m_rect.top;
    
    /* 棋盘左上角(x, y) 以及棋子的宽和高 */
    int x_start = m_rect.left;
    int y_start = m_rect.top;
    int w_distance = width / 3;
    int h_distance = height / 3;

    width和height,分别为棋盘的宽度和高度

    x_left_top和y_left_top,分别为棋盘的左上角(x, y)

    w_distance和h_distance,分别为棋子的宽度和高度

    然后进行设置棋子相应的位置

    for (int c = 0, col = 3; c < col; ++c)
    {
    	for (int r = 0, row = 3; r < row; ++r)
    	{
    		/* 创建棋盘格子,并保存到棋盘中 */
    		m_ppPreces[c * 3 + r] = new Prece(x_start + (r * w_distance), y_start + (c * h_distance), w_distance, h_distance, c * 3 + r);
    	}
    }

    两个for循环为创建3x3的棋子,col(列) row(行)

    m_ppPreces[c * 3 + r]为将棋子放入数组对应的下标中,也就是1对应棋子1、2对应棋子2···

    new Prece(x_start + (r * w_distance), y_start + (c * h_distance), w_distance, h_distance, c * 3 + r)
    Prece::Prece(int x, int y, int w, int h, int index)
    {
    	/* 格子初始化 */
    	m_x = x;
    	m_y = y;
    	m_w = w;
    	m_h = h;
    	m_index = index;
    	m_bIsClick = false;
    }

    new 棋子,创建棋子,并且初始化位置和大小以及棋子的下标(也就是1,2,3,···标识棋子位置)

    和点击状态为未点击

    PS:所以在Board::Init中为初始化棋盘大小和初始化棋盘上的9个棋子的位置等信息

     

    2) 游戏鼠标点击事件处理(TicTacToe_MouseDown)

    ① case WM_LBUTTONDOWN

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch (message)
    	{
            ······
    	case WM_LBUTTONDOWN:
    		/* 井字游戏鼠标点击消息处理 */
    		TicTacToe_MouseDown(LOWORD(lParam), HIWORD(lParam));
    		break;
    	······
    	}
    
    	return ((LRESULT)0);
    }

    lParam为附加消息(不同消息的附加消息都不同)

    低位保存x坐标(使用LOWORD获取低位数据)

    高位保存y坐标(使用HIWORD获取低位数据)

    最后将(x, y)传入TicTacToe_MouseDown

     

    ② TicTacToe_MouseDown(点击事件处理)

    VOID TicTacToe_MouseDown(int x, int y)
    {
    	/* 游戏鼠标处理 */
    	g_game.MouseDown(x, y);
    }

    传递鼠标消息给游戏对象

     

    ③Game::MouseDown(游戏点击事件处理)

    void Game::MouseDown(int x, int y)
    {
    	if (m_bIsGameOver)
    	{
    		/* 如果游戏结束,点击重新开始 */
    		Init(m_hWnd);
    		return;
    	}
    	/* 游戏鼠标点击消息处理 */
    	m_board.MouseDown(x, y);
        /* 检测是否游戏结束 */
    	CheckGameOver();
    }

    拆成小部分进行讲解:

    if (m_bIsGameOver)
    {
    	/* 如果游戏结束,点击重新开始 */
    	Init(m_hWnd);
    	return;
    }

    如果游戏已经结束,则点击为重新开始游戏

    此处的Init(m_hWnd)为上面所讲的初始化游戏

    /* 游戏鼠标点击消息处理 */
    m_board.MouseDown(x, y);

    讲鼠标消息传递到棋盘中

    /* 检测是否游戏结束 */
    CheckGameOver();

    检测是否游戏结束(判断条件为是否三个点连成一线,或9个格子都填满了)

    (CheckGameOver方法后面进行讲解,此处先了解)

     

    ④ Board::MouseDown(棋盘点击事件处理)

    void Board::MouseDown(int x, int y)
    {
    	/* 检测是否点击到格子 */
    	for (int i = 0, count = 9; i < 9; ++i)
    	{
    		if (m_ppPreces[i]->CheckClick(x, y))
    		{
    			/* 设定棋子被当前落棋玩家点击 */
    			m_ppPreces[i]->Click(m_cur_click);
    			/* 点击到格子,则切换玩家下棋 */
    			m_cur_click = (m_cur_click == ClickPlayer::ClickPlayer_Player1 ? 
    				ClickPlayer::ClickPlayer_Player2 : 
    				ClickPlayer::ClickPlayer_Player1);
    		}
    	}
    }

    for循环为遍历9个棋子

    if (m_ppPreces[i]->CheckClick(x, y))

    通过鼠标点击的位置(x, y)进行判断是否点击到棋子(Prece::CheckClick方法下面进行讲解)

    /* 设定棋子被当前落棋玩家点击 */
    m_ppPreces[i]->Click(m_cur_click);

    设定棋子被当前落棋玩家点击

    /* 点击到格子,则切换玩家下棋 */
    m_cur_click = (m_cur_click == ClickPlayer::ClickPlayer_Player1 ? 
    	ClickPlayer::ClickPlayer_Player2 : 
    	ClickPlayer::ClickPlayer_Player1);

    切换落棋玩家

     

    ⑤Prece::CheckClick(判断棋子是否被点击)

    bool Prece::CheckClick(int x, int y)
    {
    	/* 判断鼠标点击的位置是否在格子内 */
    	return (!m_bIsClick) && (x <= m_x + m_w && y <= m_y + m_h && x >= m_x && y >= m_y);
    }

    通过判断棋子是否已被点击,并且鼠标点击的(x, y)是否在棋子的矩形中

     

    ⑥Prece::Click(设定棋子被玩家点击)

    void Prece::Click(ClickPlayer sender)
    {
    	/* 格子被点击 */
    	m_bIsClick = true;
    	/* 设定点击玩家 */
    	m_click_player = sender;
    }
    

    设定棋子被点击,设定点击玩家

     

    3) 游戏渲染 (TicTacToe_Render)

    ① TicTacToe_Render(游戏渲染)

    VOID TicTacToe_Render(HWND hWnd)
    {
    	if (g_mdc == NULL)
    		return;
    
    	HDC hdc = ::GetDC(hWnd);
    	RECT clientRect;
    	::GetClientRect(hWnd, &clientRect);
    	int width = clientRect.right - clientRect.left;
    	int height = clientRect.bottom - clientRect.top;
    
    	/* 游戏绘制 */
    	g_game.Render(g_mdc);
    
    	/* 将兼容DC绘制到设备DC中 */
    	::BitBlt(hdc, 0, 0, width, height, g_mdc, 0, 0, SRCCOPY);
    	::ReleaseDC(hWnd, hdc);
    }

    GetDC()获取设备DC,GetClientRect()获取客户端区域矩形

    获取客户端的宽和高,主要为了将兼容DC绘制到设备DC中(使用了BitBlt函数)

    用了GetDC(),不要忘了使用ReleaseDC()进行释放设备DC

    /* 游戏绘制 */
    g_game.Render(g_mdc);

    绘制游戏中的游戏对象,将其绘制到兼容DC中

     

    ② Game::Render(绘制游戏中的游戏对象)

    void Game::Render(HDC hdc)
    {
    	/* 绘制游戏背景 */
    	DrawBackground(hdc);
    	/* 绘制棋盘 */
    	m_board.Render(hdc);
    	/* 绘制游戏结束 */
    	DrawGameOver(hdc);
    }

    在游戏绘制中分别有:绘制游戏背景、绘制画板、绘制游戏结束

     

    ③ Game::DrawBackground(绘制游戏背景)

    
    void Game::DrawBackground(HDC hdc)
    {
    	/* 创建背景颜色画刷 */
    	HBRUSH brush = ::CreateSolidBrush(RGB(22, 22, 22));
    	RECT rect;
    	::GetClientRect(m_hWnd, &rect);
    	/* 填充背景颜色 */
    	::FillRect(hdc, &rect, brush);
    	::DeleteObject(brush); brush = NULL;
    }

    1. 首先创建背景颜色画刷

    2. 获取窗口客户端矩形

    3. 填充背景画刷颜色到窗口客户端矩形

    4. 最后释放画刷对象

     

    ④  Game::DrawGameOver(绘制游戏结束)

    void Game::DrawGameOver(HDC hdc)
    {
    	/* 绘制游戏结束信息 */
    	if (m_bIsGameOver)
    	{
    		LPCWSTR lpszTitle = _T("游戏结束");
    		LPCWSTR lpszBody = NULL;
    		LPCWSTR lpszTips = _T("点击屏幕重新开始游戏");
    
    		/* 设置显示消息 */
    		if (m_over_type == GameOverType::GameOverType_Tie)
    			lpszBody = _T("  平局");
    		else if (m_over_type == GameOverType::GameOverType_Player1)
    			lpszBody = _T("玩家1获胜");
    		else
    			lpszBody = _T("玩家2获胜");
    
    		// 设置绘制的文字字体
    		HFONT hFont, hOldFont;
    		Util::CreateLogFont(hFont, 45);
    		hOldFont = (HFONT)SelectObject(hdc, hFont);
    
    		/* 文字背景为透明 */
    		::SetBkMode(hdc, TRANSPARENT);
    
    		/* 绘制标题 */
    		::SetTextColor(hdc, RGB(197, 31, 31));
    		::TextOut(hdc, 150, 100, lpszTitle, lstrlen(lpszTitle));
    
    		/* 绘制信息 */
    		::SetTextColor(hdc, RGB(87, 105, 60));
    		::TextOut(hdc, 150, 225, lpszBody, lstrlen(lpszBody));
    
    		/* 绘制提示消息 */
    		::SetTextColor(hdc, RGB(91, 74, 66));
    		::TextOut(hdc, 0, 350, lpszTips, lstrlen(lpszTips));
    
    		::SelectObject(hdc, hOldFont);
    		::DeleteObject(hFont); hFont = NULL;
    	}
    }

    拆分进行讲解:

    if (m_bIsGameOver)
    

    当游戏结束才进行绘制

    LPCWSTR lpszTitle = _T("游戏结束");
    LPCWSTR lpszBody = NULL;
    LPCWSTR lpszTips = _T("点击屏幕重新开始游戏");

    游戏结束页面显示:标题(lpszTitle)、获胜内容(lpszBody)、重新开始提示(lpszTips)

    /* 设置显示消息 */
    if (m_over_type == GameOverType::GameOverType_Tie)
    	lpszBody = _T("  平局");
    else if (m_over_type == GameOverType::GameOverType_Player1)
    	lpszBody = _T("玩家1获胜");
    else
    	lpszBody = _T("玩家2获胜");

    根据结局,进行显示不同的获胜内容

    // 设置绘制的文字字体
    HFONT hFont, hOldFont;
    Util::CreateLogFont(hFont, 45);
    hOldFont = (HFONT)SelectObject(hdc, hFont);

    设置文字的字体

    /* 文字背景为透明 */
    ::SetBkMode(hdc, TRANSPARENT);

    设置文字背景颜色为透明

    /* 绘制标题 */
    ::SetTextColor(hdc, RGB(197, 31, 31));
    ::TextOut(hdc, 150, 100, lpszTitle, lstrlen(lpszTitle));
    
    /* 绘制信息 */
    ::SetTextColor(hdc, RGB(87, 105, 60));
    ::TextOut(hdc, 150, 225, lpszBody, lstrlen(lpszBody));
    
    /* 绘制提示消息 */
    ::SetTextColor(hdc, RGB(91, 74, 66));
    ::TextOut(hdc, 0, 350, lpszTips, lstrlen(lpszTips));

    将标题、获胜内容、重新开始提示显示到屏幕

    SetTextColor(设置显示文字的颜色)

    TextOut(输出文字到指定位置)

    ::SelectObject(hdc, hOldFont);
    ::DeleteObject(hFont); hFont = NULL;

    恢复到之前的状态,销毁字体对象

    PS:说了这么多其实就是显示(标题、获胜内容、重新开始)

     

    ⑤ Board::Render (绘制棋盘)

    void Board::Render(HDC hdc)
    {
    	/* 绘制"井" */
    	DrawBoard(hdc);
    
    	/* 绘制棋子 */
    	for (int i = 0, count = 9; i < 9; ++i)
    	{
    		m_ppPreces[i]->Render(hdc);
    	}
    }

    在棋盘绘制中分别是绘制棋盘和绘制9个棋子

    这里的棋盘是指棋盘上面的"井"

     

    ⑥ Board::DrawBoard (绘制棋盘)

    void Board::DrawBoard(HDC hdc)
    {
    	/* 创建画笔 */
    	HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
    	HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);
    
    	/* 棋盘宽高 */
    	int width = m_rect.right - m_rect.left;
    	int height = m_rect.bottom - m_rect.top;
    	int x_left_top = m_rect.left;
    	int y_left_top = m_rect.top;
    	int w_distance = width / 3;
    	int h_distance = height / 3;
    
    	/* "井"四标边 */
    	int points[4][4];
    
    	/* 竖线第一条 */
    	points[0][0] = x_left_top + w_distance;
    	points[0][1] = y_left_top;
    	points[0][2] = x_left_top + w_distance;
    	points[0][3] = y_left_top + height;
    
    	/* 竖线第二条 */
    	points[1][0] = x_left_top + 2 * w_distance;
    	points[1][1] = y_left_top;
    	points[1][2] = x_left_top + 2 * w_distance;
    	points[1][3] = y_left_top + height;
    
    	/* 横线第一条 */
    	points[2][0] = x_left_top;
    	points[2][1] = y_left_top + h_distance;
    	points[2][2] = x_left_top + width;
    	points[2][3] = y_left_top + h_distance;
    
    	/* 横线第二条 */
    	points[3][0] = x_left_top;
    	points[3][1] = y_left_top + 2 * h_distance;
    	points[3][2] = x_left_top + width;
    	points[3][3] = y_left_top + 2 * h_distance;
    
    	Util::DrawLine(hdc, points[0]);
    	Util::DrawLine(hdc, points[1]);
    	Util::DrawLine(hdc, points[2]);
    	Util::DrawLine(hdc, points[3]);
    
    	::SelectObject(hdc, hOldPen);
    	::DeleteObject(hPen); hPen = NULL;
    }

    拆分进行讲解:

    /* 创建画笔 */
    HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
    HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);

    创建颜色画笔,使用此颜色画笔进行绘制线条

    /* 棋盘宽高 */
    int width = m_rect.right - m_rect.left;
    int height = m_rect.bottom - m_rect.top;
    int x_left_top = m_rect.left;
    int y_left_top = m_rect.top;
    int w_distance = width / 3;
    int h_distance = height / 3;

    width和height,分别为棋盘的宽度和高度

    x_left_top和y_left_top,分别为棋盘的左上角(x, y)

    w_distance和h_distance,分别为棋子的宽度和高度

    /* "井"四标边 */
    int points[4][4];
    

    定义一个4x4的数组,用于保存四条直线的起点和终点坐标(x, y)

    /* 竖线第一条 */
    points[0][0] = x_left_top + w_distance;
    points[0][1] = y_left_top;
    points[0][2] = x_left_top + w_distance;
    points[0][3] = y_left_top + height;
    
    /* 竖线第二条 */
    points[1][0] = x_left_top + 2 * w_distance;
    points[1][1] = y_left_top;
    points[1][2] = x_left_top + 2 * w_distance;
    points[1][3] = y_left_top + height;
    
    /* 横线第一条 */
    points[2][0] = x_left_top;
    points[2][1] = y_left_top + h_distance;
    points[2][2] = x_left_top + width;
    points[2][3] = y_left_top + h_distance;
    
    /* 横线第二条 */
    points[3][0] = x_left_top;
    points[3][1] = y_left_top + 2 * h_distance;
    points[3][2] = x_left_top + width;
    points[3][3] = y_left_top + 2 * h_distance;

    "井"字四条边的起点和终点坐标(x, y)

    Util::DrawLine(hdc, points[0]);
    Util::DrawLine(hdc, points[1]);
    Util::DrawLine(hdc, points[2]);
    Util::DrawLine(hdc, points[3]);

    调用Util的绘制线段(Util在后面进行讲解,此处先为了解)

    ::SelectObject(hdc, hOldPen);
    ::DeleteObject(hPen); hPen = NULL;

    恢复为先前状态,销毁画笔对象

     

    ⑦ Prece::Render (绘制棋子)

    void Prece::Render(HDC hdc)
    {
    	/* 绘制标记 */
    	DrawGraphics(hdc);
    }
    

    在绘制棋子中为绘制标记

     

    ⑧ Prece::DrawGraphics(绘制棋子标记)

    void Prece::DrawGraphics(HDC hdc)
    {
    	/* 判断棋子是否被玩家点击 */
    	if (!m_bIsClick)
    		return;
    
    	if (m_click_player == ClickPlayer::ClickPlayer_Player1)
    	{
    		/* 绘制玩家1图形 */
    		DrawPlayer1Graphics(hdc);
    	}
    	else
    	{
    		/* 绘制玩家2图形 */
    		DrawPlayer2Graphics(hdc);
    	}
    }

    拆分进行讲解:

    /* 判断棋子是否被玩家点击 */
    	if (!m_bIsClick)
    		return;

    当棋子没有被玩家点击,不进行绘制标记(因为只有点击了才会有玩家的标记)

    if (m_click_player == ClickPlayer::ClickPlayer_Player1)
    {
    	/* 绘制玩家1图形 */
    	DrawPlayer1Graphics(hdc);
    }

    如果棋子为玩家1点击,则绘制玩家1的标记

    else
    {
    	/* 绘制玩家2图形 */
    	DrawPlayer2Graphics(hdc);
    }

    否则为玩家2点击,则绘制玩家2的标记

     

    ⑨ Prece::DrawPlayer1Graphics (绘制玩家1标记)

    void Prece::DrawPlayer1Graphics(HDC hdc)
    {
    	// 棋子中心点坐标
    	int x_center = m_x + (m_w / 2);
    	int y_center = m_y + (m_h / 2);
    
    	/* 绘制 "×" */
    	double len = m_w / 3.0;
    	float angles[] = {
    		45, 135, 225, 315
    	};
    
    	int points[2][4];
    
    	float rad = 3.1415926f / 180.0f;
    	/* 第一条 */
    	int x_lt = (int)(x_center + len * cos(angles[0] * rad));
    	int y_lt = (int)(y_center + len * sin(angles[0] * rad));
    	int x_rd = (int)(x_center + len * cos(angles[2] * rad));
    	int y_rd = (int)(y_center + len * sin(angles[2] * rad));
    
    	/* 第二条 */
    	int x_rt = (int)(x_center + len * cos(angles[1] * rad));
    	int y_rt = (int)(y_center + len * sin(angles[1] * rad));
    	int x_ld = (int)(x_center + len * cos(angles[3] * rad));
    	int y_ld = (int)(y_center + len * sin(angles[3] * rad));
    
    	points[0][0] = x_lt;
    	points[0][1] = y_lt;
    	points[0][2] = x_rd;
    	points[0][3] = y_rd;
    
    	points[1][0] = x_rt;
    	points[1][1] = y_rt;
    	points[1][2] = x_ld;
    	points[1][3] = y_ld;
    
    	HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(153, 77, 82));
    	HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);
    	/* 绘制 */
    	Util::DrawLine(hdc, points[0]);
    	Util::DrawLine(hdc, points[1]);
    
    	::SelectObject(hdc, hOldPen);
    	::DeleteObject(hPen); hPen = NULL;
    }

    拆分进行讲解:

    // 棋子中心点坐标
    int x_center = m_x + (m_w / 2);
    int y_center = m_y + (m_h / 2);

    计算出棋子的中心点坐标(x, y)

    double len = m_w / 3.0;

    表示"×"两条线段的长度的一半

    也就是比如这个"\"为两条线段其中一条,则此len变量表示这条线段的一半

    float angles[] = {
    	45, 135, 225, 315
    };

    四个点的角度

    int points[2][4];

    两条线段的起点和终点的坐标(x, y)

    float rad = 3.1415926f / 180.0f;

    计算1°相对的弧度

    /* 第一条 */
    int x_lt = (int)(x_center + len * cos(angles[0] * rad));
    int y_lt = (int)(y_center + len * sin(angles[0] * rad));
    int x_rd = (int)(x_center + len * cos(angles[2] * rad));
    int y_rd = (int)(y_center + len * sin(angles[2] * rad));
    
    /* 第二条 */
    int x_rt = (int)(x_center + len * cos(angles[1] * rad));
    int y_rt = (int)(y_center + len * sin(angles[1] * rad));
    int x_ld = (int)(x_center + len * cos(angles[3] * rad));
    int y_ld = (int)(y_center + len * sin(angles[3] * rad));
    
    points[0][0] = x_lt;
    points[0][1] = y_lt;
    points[0][2] = x_rd;
    points[0][3] = y_rd;
    
    points[1][0] = x_rt;
    points[1][1] = y_rt;
    points[1][2] = x_ld;
    points[1][3] = y_ld;

    通过三角函数计算出四个点的坐标(x, y)

    其中lt(left-top)、rt(right-top)、ld(left-down)、rd(right-down)

    HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(153, 77, 82));
    HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);

    创建颜色画笔,并且使用此画笔

    /* 绘制 */
    Util::DrawLine(hdc, points[0]);
    Util::DrawLine(hdc, points[1]);

    进行绘制两条线段线段

    ::SelectObject(hdc, hOldPen);
    ::DeleteObject(hPen); hPen = NULL;

    恢复到先前的状态,并且销毁画笔对象

     

    ⑩ Prece::DrawPlayer2Graphics (绘制玩家2标记)

    void Prece::DrawPlayer2Graphics(HDC hdc)
    {
    	/* 棋子中心点坐标 */
    	int x_center = m_x + (m_w / 2);
    	int y_center = m_y + (m_h / 2);
    
    	/* "○"半径 */
    	int r = m_w / 3;
    	/* 绘制 "○" */
    	HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(64, 116, 52));
    	HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);
    	HBRUSH hBrush = (HBRUSH)::GetStockObject(NULL_BRUSH);
    	HBRUSH bOldBrush = (HBRUSH)::SelectObject(hdc, hBrush);
    
    	::Ellipse(hdc, x_center - r, y_center - r, x_center + r, y_center + r);
    
    	::SelectObject(hdc, bOldBrush);
    	::SelectObject(hdc, hOldPen);
    	::DeleteObject(hBrush); hBrush = NULL;
    	::DeleteObject(hPen); hPen = NULL;
    }

    拆分进行讲解:

    /* 棋子中心点坐标 */
    int x_center = m_x + (m_w / 2);
    int y_center = m_y + (m_h / 2);
    

    计算出棋子的中心坐标(x, y)

    /* "○"半径 */
    int r = m_w / 3;

    定义圆的半径

    HPEN hPen = ::CreatePen(PS_SOLID, 3, RGB(64, 116, 52));
    HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);
    HBRUSH hBrush = (HBRUSH)::GetStockObject(NULL_BRUSH);
    HBRUSH bOldBrush = (HBRUSH)::SelectObject(hdc, hBrush);

    创建颜色画笔,并选用此画笔

    创建空心画刷,并选用此画刷(选用空心画刷就是让圆的中心为透明,不填充颜色)

    ::Ellipse(hdc, x_center - r, y_center - r, x_center + r, y_center + r);
    

    绘制圆

    ::SelectObject(hdc, bOldBrush);
    ::SelectObject(hdc, hOldPen);
    ::DeleteObject(hBrush); hBrush = NULL;
    ::DeleteObject(hPen); hPen = NULL;

    恢复先前的状态,并销毁画笔和画刷对象

     

    4) Game::CheckGameOver (检测游戏是否结束)

    void Game::CheckGameOver()
    {
    	/* 获取棋盘格子 */
    	Prece** ppPreces = m_board.GetPreces();
    
    	/* 获取玩家点击了的格子 */
    	int p1[9];
    	int p2[9];
    	memset((void*)p1, -1, sizeof(p1));
    	memset((void*)p2, -1, sizeof(p2));
    
    	int index1 = 0;
    	int index2 = 0;
    	for (int i = 0, count = 9; i < count; ++i)
    	{
    		if (ppPreces[i] != NULL && ppPreces[i]->IsClick())
    		{
    			ppPreces[i]->GetClickPlayer() == ClickPlayer::ClickPlayer_Player1 ?
    				p1[index1++] = ppPreces[i]->GetIndex() :
    				p2[index2++] = ppPreces[i]->GetIndex();
    		}
    	}
    
    	/* 不足3个取消比较 */
    	if (index1 < 3 && index2 < 3)
    		return;
    	
    	/* 8种获胜结果集合 */
    	int win_set[8][3] = {
    		{0, 1, 2},
    		{3, 4, 5},
    		{6, 7, 8},
    
    		{0, 3, 6},
    		{1, 4, 7},
    		{2, 5, 8},
    
    		{0, 4, 8},
    		{2, 4, 6}
    	};
    
    	/* 进行比较 */
    	int nP1Match = 0;
    	int nP2Match = 0;
    	for (int i = 0; i < 8; ++i)
    	{
    		nP1Match = 0;
    		nP2Match = 0;
    		for (int j = 0; j < 3; ++j)
    		{
    			for (int k = 0; k < index1; ++k)
    			{
    				if (p1[k] == win_set[i][j])
    					++nP1Match;
    				else if (p2[k] == win_set[i][j])
    					++nP2Match;
    
    				if (nP1Match == 3)
    				{
    					m_over_type = GameOverType::GameOverType_Player1;
    					m_bIsGameOver = true;
    					return;
    				}
    				else if (nP2Match == 3)
    				{
    					m_over_type = GameOverType::GameOverType_Player2;
    					m_bIsGameOver = true;
    					return;
    				}
    			}
    		}
    	}
    
    	/* 9个为平局 */
    	if (index1 + index2 >= 9)
    	{
    		m_over_type = GameOverType::GameOverType_Tie;
    		m_bIsGameOver = true;
    	}
    
    }
    

    拆分进行讲解:

    /* 获取棋盘格子 */
    Prece** ppPreces = m_board.GetPreces();

    获取棋盘上的9个棋子

    /* 获取玩家点击了的格子 */
    int p1[9];
    int p2[9];
    memset((void*)p1, -1, sizeof(p1));
    memset((void*)p2, -1, sizeof(p2));

    定义p1、p2两个数组进行保存 玩家1 玩家2 点击的棋子下标,并设置数组中的元素为-1

    int index1 = 0;
    int index2 = 0;
    for (int i = 0, count = 9; i < count; ++i)
    {
    	if (ppPreces[i] != NULL && ppPreces[i]->IsClick())
    	{
    		ppPreces[i]->GetClickPlayer() == ClickPlayer::ClickPlayer_Player1 ?
    			p1[index1++] = ppPreces[i]->GetIndex() :
    			p2[index2++] = ppPreces[i]->GetIndex();
    	}
    }

    遍历9个棋子,将点击的棋子保存到对应的数组中

    /* 不足3个取消比较 */
    if (index1 < 3 && index2 < 3)
    	return;

    如果棋子点击数量不足三个,则不进行比较(因为最低也要三个棋子连成一线)

    /* 8种获胜结果集合 */
    int win_set[8][3] = {
    	{0, 1, 2},
    	{3, 4, 5},
    	{6, 7, 8},
    
    	{0, 3, 6},
    	{1, 4, 7},
    	{2, 5, 8},
    
    	{0, 4, 8},
    	{2, 4, 6}
    };

    列举八种胜利方式

    int nP1Match = 0;
    int nP2Match = 0;

    定义玩家1玩家2匹配的数量

    for (int i = 0; i < 8; ++i)
    {
        nP1Match = 0;
        nP2Match = 0;
        for (int j = 0; j < 3; ++j)
        {
            for (int k = 0; k < index1; ++k)
            {
    	       ......
            }
        }
    }

    每次进行一种胜利方式匹配前,都重新设置玩家1玩家2的匹配数为0

    变量八种胜利方式,然后遍历每种里面的三个棋子下标,

    遍历玩家1和玩家2所下的所有棋子,如果存在此种组合的三个棋子下标则获胜

    if (p1[k] == win_set[i][j])
        ++nP1Match;

    如果玩家1存在此种组合的其中一个下标,则对匹配变量(nP1Match)进行++

    else if (p2[k] == win_set[i][j])
        ++nP2Match;

    如果玩家2存在此种组合的其中一个下标,则对匹配变量(nP2Match)进行++

    if (nP1Match == 3)
    {
        m_over_type = GameOverType::GameOverType_Player1;
        m_bIsGameOver = true;
        return;
    }
    else if (nP2Match == 3)
    {
        m_over_type = GameOverType::GameOverType_Player2;
        m_bIsGameOver = true;
        return;
    }

    如果匹配数量为3,则为八种获胜方式中的其中一种方式中的下标全部匹配,那么此玩家获胜

    设置游戏结束类型(m_over_type = ···)(在绘制游戏结束中使用)

    并且设置游戏结束(m_bIsGameOver = true)

    /* 9个为平局 */
    if (index1 + index2 >= 9)
    {
    	m_over_type = GameOverType::GameOverType_Tie;
    	m_bIsGameOver = true;
    }

    如果上面两个玩家都没有匹配成功,并且九个棋子都被玩家点击,那么为平局

    设置游戏结束类型(m_over_type = ···)(在绘制游戏结束中使用)

    并且设置游戏结束(m_bIsGameOver = true)

     

    5) Util (常用工具)类

    是否发现在上面经常使用到了Util这个类呢。

    正是因为经常使用,所以避免太多重复代码,故添加此类

    PS:以后游戏中会继续使用此Util,并且不断进行添加常用的方法

    在Util中定义了三个方法,分别是:

    static void DrawLine(HDC, int[4]);                          // 绘制一条直线
    static void CreateDoubleBuffer(HWND, HDC &, HBITMAP &);     // 创建创缓冲
    static void CreateLogFont(HFONT &, int);                    // 创建逻辑字体

     

    ① Util::DrawLine (绘制线段)

    void Util::DrawLine(HDC hdc, int points[4])
    {
    	/* *
    	 * int[4] 表示两个点的 (x, y) 
    	 * 第一个点为 (points[0], points[1])
    	 * 第二个点为 (points[2], points[3])
    	 * */
    
    	::MoveToEx(hdc, points[0], points[1], NULL);
    	::LineTo(hdc, points[2], points[3]);
    }

    通过MoveToEx移动到线段起点,在通过LineTo绘制一条从起点到终点的线段

     

    ②  Util::CreateDoubleBuffer (创建双缓冲)

    void Util::CreateDoubleBuffer(HWND hWnd, HDC &mdc, HBITMAP &bitmap)
    {
    	/* * 
    	 * 创建双缓冲
    	 * 也就是: 兼容DC和兼容位图
    	 * */
    	HDC hdc = ::GetDC(hWnd);
    	RECT clientRect;
    	::GetClientRect(hWnd, &clientRect);
    	mdc = ::CreateCompatibleDC(hdc);
    	bitmap = ::CreateCompatibleBitmap(hdc, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
    
    	::ReleaseDC(hWnd, hdc);
    }

    1. 通过GetDC获取设备DC

    2. 获取客户端矩形大小

    3. 通过CreateCompatibleDC创建兼容DC

    4. 通过CreateCompatibleBitmap创建一张跟客户端矩形大小的兼容位图

    5. 最后不要忘了ReleaseDC,释放获取的设备DC

     

    ③ Util::CreateLogFont (创建逻辑字体)

    void Util::CreateLogFont(HFONT &hFont, int nFontHeight)
    {
    	/* * 
    	 * 创建逻辑字体
    	 * */
    	LOGFONT logfont;
    	ZeroMemory(&logfont, sizeof(LOGFONT));
    	logfont.lfCharSet = GB2312_CHARSET;
    	logfont.lfHeight = nFontHeight;
    	hFont = ::CreateFontIndirect(&logfont);
    }

    1. 定义一个逻辑字体

    2. 设置逻辑字体的字符集为GB_2312(详细见GB_2312百度百科)

    3. 设置逻辑字体的高度为参数的高度

    4. 创建此逻辑字体

     

    ヽ( ̄▽ ̄)و,井字游戏就此介绍完毕啦,下面贴游戏图吧,(#^.^#)

    井字游戏运行图井字游戏结束图

     

     

    源代码:

    避免有些没有C币的小伙伴(就像我),所以将源代码放到Github啦~

    Github地址: Github

     

     

     

     

     

    展开全文
  • 虽然这是一个三维数组, 我们把它拆分下就好理解了 相当于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1] 因为对象可以这样取值: var obj = { a: 10...
  • ] ] 虽然这是一个三维数组, 我们把它拆分下就好理解了 相当于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1] 因为对象可以这样取值: var obj = { a: 10, ...
  • 记忆拼图 如何玩记忆拼图 在 Memory Puzzle 游戏中,几个图标被白框覆盖。每个图标有两个。玩家可以单击两个框来查看它们后面的图标。如果图标匹配,则这些框保持未覆盖。当棋盘上的所有盒子都被揭开时,玩家获胜。...

空空如也

空空如也

1
收藏数 7
精华内容 2
关键字:

drawboard拆分