C语言课程设计
目录
实现目的
游戏玩法介绍
实现流程与作品架构
任务列表及贡献度
总结感想
作品源码与仓库地址(附页)
资料引用与出处(附页)
实现目的
2048,作为一款极其经典的游戏,从发行到现在,已经有了极多的版本和玩法,各种优秀应用游戏层出不穷。
本游戏注重"视觉展现效果和更快速的动画"、注重数据储存,是一种对于算法和 UI 设计有一定要求的应用设计。
本小组秉承课程所学与外部优秀知识技术相结合的观念,多人合作,共同研究学习。
以 C
语言为基础、C++
语言库函数为辅助、Windows
程式应用编程和状态机思想为框架、搜索和多线程运行等算法知识为技术核心。
游戏玩法介绍
- 选择上下左右任何一个方向去滑动
- 每滑动一次,所有的数字方块都会往滑动的方向靠拢外
- 系统在滑动之后会在空白的地方随机出现一个数字方块(一般是2或4)
- 相同数字的方块会相加成一个数字
- 不断的叠加最终拼凑出2048这个数字就算成功。
实现流程与作品架构
- 使用
Windows
窗口编程,创建窗口 - 在消息循环中创建
Game
程序实现主体,使用C++
语言中new
创建Game
类,用于游戏的初始化 Game
类包含以下内容- 窗口创建时的一些属性
- 游戏初始化部分
- 游戏运行时四个方向的移动函数
- 移动判断函数
- 图片的绘制函数
- 定时器的程序运行
- 游戏移动消息循环接受和对应操作的实现
- 在不断进行的消息循环中,进行绘图的操作(不停的擦除和重绘)
任务列表及贡献度
任务内容 | 任务说明 | 任务贡献度 | 任务截至日期 |
---|---|---|---|
游戏进程 | 游戏主体进程的详细讨论,包括详细的子项 | 5 | 2019.5.12 |
美术大纲 | 讨论游戏整体美术风格和单块素材美术风格 | 5 | 2019.5.12 |
素材收集及制作 | 按照美术大纲,收集或制作全部要求的素材 | 30 | 2019.5.25 |
状态机实现 | 根据游戏进程,实现个状态之间的无素材转换 | 15 | 2019.5.15 |
Windows子窗口控件实现 | 根据游戏进程、美术大纲,实现个状态之间无素材子窗口内容 | 20 | 2019.5.20 |
游戏框架制作 | 收集前期全部工作成果,写出游戏框架 | 25 | 2019.6.1 |
游戏实例代码编写 | 根据游戏框架编写全部所需子代码 | 10/子代码 | 2019.6.5 |
游戏总制作 | 收集全部代码,写出总程序 | 15 | 2019.6.7 |
制作完成项目总结及成果整理 | 项目总结 | 3 | 2019.6.9 |
项目报告 | 用于C语言课程设计和应用程序设计比赛 | 5 | 2019.6.10 |
总结感想
JingWenxing's
这次课程设计,是编程生涯的第一次多人合作。虽然之前对于多人合作有一定的感想和思考,但是实践出真知,我还是发现了很多问题。
作为一个多人合作的队伍,首先需要的就是任务的细化和代码沟通的通常。这点我们并没有做的很好。任务一直没有很好的定下来,并且个人任务混杂,互相干涉,效率不高。代码沟通上之前并没有使用“码云”,导致每次都要自行寻找各版本中的不同之处。
除此之外就是任务逾期的问题。几乎所有的项目,都没有在规定的时间内完成,都有多多少少的拖沓情况。这在多人合作中是致命的。这也是今后需要极为注意的地方
自身的知识水平和技术也是一大问题。“现学现用”在本次合作中可以说是贯穿全场。不论是C++、Windows,还是EasyX,状态机。虽然学习了新的东西收获慢慢,但是仍然发现了自己技术上的不足。
暑假里我将完成以下的学习,继续深造:
学习任务 | 学习任务 | 学习任务 |
---|---|---|
计算机网络 | Linux | DirectX 3D 11 |
Google test | Java | C / C++ |
算法 | 英语 | 数学 |
Crush‘s
在这次以小组为单位的课程设计中,我觉得有收获也有所欠缺,课程设计本身就是一个问题,需要我们小组成员一起去解决的问题,我们大家给出自己的想法和思路怎么去把这个课程设计完美的做出来,因此每个人可能需要分配一些各自的任务,然后各自去完成自己负责的部分,起初我认为这样是比较合理的。
但是在完成各自任务的过程中,我觉得还是需要大家一起去讨论才能够选出最佳方案,比如游戏的美工和界面,就需要征集大家的意见,才能确定一个适合大众口味的风格。
确定好之后再分配到个人去搜集素材并整理好,这个过程中,需要用到一些相关软件,如ps,但我们对ps的操作还是有很多不会的地方,所以还需要加强这方面的学习。
还有就是大家的积极性可能会因为无法实现自己想要表达的效果而减退,很多时候无法在规定的时间内完成任务,导致进度过慢,这点在今后的小组课程设计中一定要改正,小组成员要时刻保持绝对的积极性,面对困难不退缩,迎难而上,认真完成课程设计任务。
LILI’s
这次的课程设计是我学习专业以来,自己的第一次实际操作,在这次小组课程设计中,我学到了许多东西,也发现了许多问题,和自己的许多不足之处。
在这次设计中,我发觉最大的问题就是我们对游戏的设计并不坚定,总是一次又一次的推翻自己之前的想法。在知道要课程设计一个游戏时,首先我们对游戏的主题结构进行了构思,在第一次游戏构思时,队长和我通过天马行空的想象,将一个完整的故事线构思好了,再将其细节也进行了细化,但在实际操作的过程中,发现游戏所需素材太多,操作困难,然后迅速放弃,一切重头再来。在这期间我们浪费了许多的时间。
在游戏设计的过程中,发现自己不懂的实在是太多,所以我也在一直不断的学习(因为我就是菜鸡本菜),除了对c的知识更加深入的学习以外,我还学习了c++类等知识。虽然我还是有很多的不足,但会继续努力。
作品源码与仓库地址
宏与头文件集成
// framework.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
#define COUTENDL(_text_) do \
{ \
std::cout << _text_ << std::endl; \
} while (false) \
#define COUT(_text_) do \
{ \
std::cout << _text_ ; \
} while (false) \
#include <SDKDDKVer.h>
// Windows 头文件
#include <windows.h>
// C 运行时头文件
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <time.h>
// TODO: 在此处引用程序需要的其他头文件
#include "Utils.h"
#include "Game.h"
// Resource.h
#define IDC_MYICON 2
#define IDD_MY2048_DIALOG 102
#define IDD_MY2048_INFO 102
#define IDS_APP_TITLE 103
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_MY2048 107
#define IDI_SMALL 108
#define IDC_MY2048 109
#define IDR_MAINFRAME 128
#define IDQUIT 1000
#define ID_sadf 1000
#define IDC_STATIC -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 131
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1002
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
// targetver.h
#include <SDKDDKVer.h>
2048 Game头与其源文件
// 2048 Game.h
#pragma once
#include "resource.h"
// 2048 Game.cpp
// 2048 Game.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "2048 Game.h"
#include <cstdio>
#define MAX_LOADSTRING 100
#define MAX_TIME 15 * 60 * 100
// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
Game* game; // 游戏主程序
UINT timerID; // 定时器ID
UINT times; // 倒计时
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK Info(HWND, UINT, WPARAM, LPARAM);
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_MY2048, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); // 注册窗口类
执行应用程序初始化:
if (!InitInstance(hInstance, nCmdShow)) // 保存实例句柄并创建主窗口
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY2048));
// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY2048));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
// wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MY2048);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // 将实例句柄存储在全局变量中
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, 0, 450, 600, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
// Init game
game = new Game();
// Init time
times = MAX_TIME;
// Set timer
timerID = SetTimer(hWnd, NULL, 1000, TimerProc);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_KEYDOWN:
switch (wParam)
{
case 'A':
case 'H':
case VK_LEFT:
if (!game->canMove(MoveCommandLeft)) break;
game->doMove(MoveCommandLeft);
InvalidateRect(hWnd, &game->chessboardRect, true);
InvalidateRect(hWnd, &game->scoreLabelRect, false);
break;
case 'D':
case 'L':
case VK_RIGHT:
if (!game->canMove(MoveCommandRight)) break;
game->doMove(MoveCommandRight);
InvalidateRect(hWnd, &game->chessboardRect, true);
InvalidateRect(hWnd, &game->scoreLabelRect, false);;
break;
case 'W':
case 'K':
case VK_UP:
if (!game->canMove(MoveCommandUp)) break;
game->doMove(MoveCommandUp);
InvalidateRect(hWnd, &game->chessboardRect, true);
InvalidateRect(hWnd, &game->scoreLabelRect, false);
break;
case 'S':
case 'J':
case VK_DOWN:
if (!game->canMove(MoveCommandDown)) break;
game->doMove(MoveCommandDown);
InvalidateRect(hWnd, &game->chessboardRect, true);
InvalidateRect(hWnd, &game->scoreLabelRect, false);
break;
case 'R':
if (game->isTerminated()) game->restart();
break;
default:
break;
}
if (game->isTerminated()) {
KillTimer(hWnd, timerID); // Kill timer
DialogBox(hInst, MAKEINTRESOURCE(IDD_MY2048_DIALOG), hWnd, Info); // Game over or win
}
break;
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
// Get client rect
RECT rect;
GetClientRect(hWnd, &rect);
game->setRect(rect);
game->draw(ps.hdc);
// Draw time
RECT timeRect = game->scoreLabelRect;
long width = getRectWidth(timeRect);
timeRect.left -= width;
timeRect.right -= width;
// Draw time
WCHAR buffer[10];
getTimeStringByValue(times, buffer);
drawNumberOnTimeLabel(ps.hdc, timeRect, buffer, RGB(249, 246, 242));
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
// “提醒”框的消息处理程序。
INT_PTR CALLBACK Info(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG: {
HWND stDisplay = GetDlgItem(hDlg, IDC_STATIC);
wchar_t buffer[256];
if (game->isWon()) {
wsprintfW(buffer, L"You win!!!\nYour score: %d", game->getScore());
}
else {
if (times == 0) {
wsprintfW(buffer, L"Time is over! You lose :(\nYour score: %d", game->getScore());
}
else {
wsprintfW(buffer, L"You lose :(\nYour score: %d", game->getScore());
}
}
SendMessage(stDisplay, WM_SETTEXT, NULL, (WPARAM)buffer);
}
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDRETRY) {
HWND hParentWnd = GetParent(hDlg);
times = MAX_TIME;
timerID = SetTimer(hParentWnd, NULL, 1000, TimerProc); // Reset timer
// Restart the game
game->restart(); // Restart
EndDialog(hDlg, LOWORD(wParam)); // Close dialog
InvalidateRect(hParentWnd, &game->chessboardRect, true); // Repaint chessboard
InvalidateRect(hParentWnd, &game->scoreLabelRect, false); // Repaint score label
RECT timeRect = game->scoreLabelRect;
long width = getRectWidth(timeRect);
timeRect.left -= width;
timeRect.right -= width;
InvalidateRect(hParentWnd, &timeRect, false); // Repatin timer
return (INT_PTR)TRUE;
}
else if (LOWORD(wParam) == IDNO) {
HWND hParentWnd = GetParent(hDlg);
EndDialog(hDlg, LOWORD(wParam));
DestroyWindow(hParentWnd); // Close main window, exit
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
if (game->isTerminated()) return;
if (times == 0) {
game->stop(); // Stop game
return;
}
times -= 100;
RECT timeRect = game->scoreLabelRect;
long width = getRectWidth(timeRect);
timeRect.left -= width;
timeRect.right -= width;
InvalidateRect(hwnd, &timeRect, false);
}
Game头与其源文件
// Game.h
#pragma once
#ifndef ___048__Game__
#define ___048__Game__
#include "framework.h"
typedef enum {
MoveCommandUp,
MoveCommandDown,
MoveCommandLeft,
MoveCommandRight
} MoveCommand;
class Game {
private:
int score;
public:
int getScore();
private:
bool over;
bool won;
int numberOfEmptyCells;
static const int size = 4;
static const int numberOfStartCells = 2;
long chessboardWidth;
int chessboard[4][4];
public:
RECT chessboardRect;
RECT scoreLabelRect;
private:
RECT topBarRect;
private:
void initChessboard();
void initStartCells();
void addRandomCell();
int getRandomAvailableCell() const;
void clearCell(const int& row, const int& col);
void getCellColor(const int& value, LPCOLORREF fontColor, LPCOLORREF cellBgColor) const;
#pragma mark Can Move?
public:
bool canMove() const;
bool canMove(const MoveCommand cmd) const;
private:
bool canMoveUp() const;
bool canMoveDown() const;
bool canMoveLeft() const;
bool canMoveRight() const;
#pragma mark -
#pragma mark Constructor & Destructor
public:
Game();
Game(RECT clientRect);
~Game();
#pragma mark -
#pragma mark Actions
public:
void doMove(const MoveCommand cmd);
private:
void doUp();
void doDown();
void doLeft();
void doRight();
public:
void restart();
void stop();
#pragma mark -
#pragma mark View
public:
void print() const;
void printHelpInfo() const;
void draw(HDC hdc) const;
private:
void drawTopBar(HDC hdc) const;
void drawLogoText(HDC hdc) const;
void drawScoreLabel(HDC hdc) const;
void drawChessboard(HDC hdc) const;
void drawCell(HDC hdc, const int& row, const int& col, const int& value) const;
public:
void setRect(const RECT clientRect);
#pragma mark -
#pragma mark Is over or won or terminated?
public:
bool isOver() const;
bool isWon() const;
bool isTerminated() const;
};
#endif /* defined(___048__Game__) */
// Game.cpp
#include "framework.h"
#include <iostream>
#include <cstdio>
#include <ctime>
int Game::getScore() {
return this->score;
}
void Game::initChessboard() {
this->score = 0;
this->over = false;
this->won = false;
this->numberOfEmptyCells = 16;
// Init chessboard
memset(this->chessboard, 0, 4 * 4 * sizeof(int));
// Add the initial cells
this->initStartCells();
// Print
this->print();
}
void Game::initStartCells() {
for (int i = 0; i < this->numberOfStartCells; i++) {
this->addRandomCell();
}
}
void Game::addRandomCell() {
int random = rand();
int value = ((double)random / RAND_MAX) < 0.9 ? 2 : 4;
int index = this->getRandomAvailableCell();
this->chessboard[index / this->size][index % this->size] = value;
this->numberOfEmptyCells--;
}
int Game::getRandomAvailableCell() const {
while (true) {
int random = rand() % (this->size * this->size);
int row = random / this->size;
int col = random % this->size;
if (this->chessboard[row][col] == 0)
return random;
}
return -1;
}
void Game::clearCell(const int& row, const int& col) {
this->chessboard[row][col] = 0;
this->numberOfEmptyCells++;
}
void Game::getCellColor(const int& value, LPCOLORREF pFontColor, LPCOLORREF pCellBgColor) const {
switch (value) {
case 0: {
int bgR = 187, bgG = 173, bgB = 160;
int R = 238, G = 228, B = 218;
int alpha = 90;
R = R * alpha / 255 + bgR * (255 - alpha) / 255;
G = G * alpha / 255 + bgG * (255 - alpha) / 255;
B = B * alpha / 255 + bgB * (255 - alpha) / 255;
*pCellBgColor = RGB(R, G, B);
}
break;
case 2:
*pFontColor = RGB(119, 110, 101);
*pCellBgColor = RGB(238, 228, 218);
break;
case 4:
*pFontColor = RGB(119, 110, 101);
*pCellBgColor = RGB(237, 224, 200);
break;
case 8:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(242, 177, 121);
break;
case 16:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(245, 149, 99);
break;
case 32:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(246, 124, 95);
break;
case 64:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(246, 94, 59);
break;
case 128:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(237, 207, 114);
break;
case 256:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(237, 204, 97);
break;
case 512:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(237, 200, 80);
break;
case 1024:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(237, 197, 63);
break;
case 2048:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(237, 194, 46);
break;
default:
*pFontColor = RGB(249, 246, 242);
*pCellBgColor = RGB(60, 58, 50);
break;
}
}
#pragma mark -
#pragma mark Can Move?
bool Game::canMove() const {
return this->canMoveUp() || this->canMoveDown() || this->canMoveLeft() || this->canMoveRight();
}
bool Game::canMove(const MoveCommand cmd) const {
switch (cmd) {
case MoveCommandLeft:
return this->canMoveLeft();
break;
case MoveCommandUp:
return this->canMoveUp();
break;
case MoveCommandRight:
return this->canMoveRight();
break;
case MoveCommandDown:
return this->canMoveDown();
break;
default:
break;
}
return false;
}
bool Game::canMoveUp() const {
for (int j = 0; j < this->size; j++) {
bool result = false;
for (int i = 0; i < this->size; i++) {
if (this->chessboard[i][j] == 0)
result = true;
else if ((i != 0 && this->chessboard[i][j] == this->chessboard[i - 1][j]) || result)
return true;
}
}
return false;
}
bool Game::canMoveDown() const {
for (int j = 0; j < this->size; j++) {
bool result = false;
for (int i = this->size - 1; i >= 0; i--) {
if (this->chessboard[i][j] == 0)
result = true;
else if ((i != this->size - 1 && this->chessboard[i][j] == this->chessboard[i + 1][j]) || (result))
return true;
}
}
return false;
}
bool Game::canMoveLeft() const {
for (int i = 0; i < this->size; i++) {
bool result = false;
for (int j = 0; j < this->size; j++) {
if (this->chessboard[i][j] == 0)
result = true;
else if ((j != 0 && this->chessboard[i][j] == this->chessboard[i][j - 1]) || (result))
return true;
}
}
return false;
}
bool Game::canMoveRight() const {
for (int i = 0; i < this->size; i++) {
bool result = false;
for (int j = this->size - 1; j >= 0; j--) {
if (this->chessboard[i][j] == 0)
result = true;
else if ((j != this->size - 1 && this->chessboard[i][j] == this->chessboard[i][j + 1]) || (result))
return true;
}
}
return false;
}
#pragma mark -
#pragma mark Constructor & Destructor
Game::Game() {
this->printHelpInfo(); // Help info
srand((unsigned)time(0));
this->initChessboard(); // Init chessboard
}
Game::Game(RECT clientRect) {
srand((unsigned)time(0));
this->initChessboard(); // Init chessboard
this->setRect(clientRect); // Set chessboard rect
}
Game::~Game() {
}
#pragma mark -
#pragma mark Actions
void Game::doMove(const MoveCommand cmd) {
// if (!this->canMove(cmd)) return;
switch (cmd) {
case MoveCommandLeft:
this->doLeft();
break;
case MoveCommandUp:
this->doUp();
break;
case MoveCommandRight:
this->doRight();
break;
case MoveCommandDown:
this->doDown();
break;
default:
break;
}
std::cout << (cmd == MoveCommandUp ? "Move Up" : (cmd == MoveCommandDown ? "Move Down" : (cmd == MoveCommandLeft ? "Move Left" : "Move Right"))) << std::endl;
this->addRandomCell();
if (this->numberOfEmptyCells == 0 && !this->canMove()) this->over = true; // Game over
this->print();
}
void Game::doUp() {
for (int j = 0; j < this->size; j++) {
int l = 0, m = -1, n = -1;
for (int i = 0; i < this->size; i++) {
if (this->chessboard[i][j] != 0) {
if (m == -1) {
m = i;
}
else {
if (n == -1) {
n = i;
if (this->chessboard[m][j] == this->chessboard[n][j]) {
// Merge
this->chessboard[l][j] = this->chessboard[m][j] * 2;
if (this->chessboard[l][j] == 2048) this->won = true; // Win!!!
if (m != l) {
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
this->clearCell(n, j);
// Add score
this->score += this->chessboard[l][j];
m = -1;
}
else {
// Move
if (m != l) {
this->chessboard[l][j] = this->chessboard[m][j];
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
m = n;
}
l++;
n = -1;
}
}
}
}
if (m != -1 && m != l) {
this->chessboard[l][j] = this->chessboard[m][j];
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
}
}
void Game::doDown() {
for (int j = 0; j < this->size; j++) {
int l = this->size - 1, m = -1, n = -1;
for (int i = this->size - 1; i >= 0; i--) {
if (this->chessboard[i][j] != 0) {
if (m == -1) {
m = i;
}
else {
if (n == -1) {
n = i;
if (this->chessboard[m][j] == this->chessboard[n][j]) {
// Merge
this->chessboard[l][j] = this->chessboard[m][j] * 2;
if (this->chessboard[l][j] == 2048) this->won = true; // Win!!!
if (m != l) {
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
this->clearCell(n, j);
// Add score
this->score += this->chessboard[l][j];
m = -1;
}
else {
// Move
if (m != l) {
this->chessboard[l][j] = this->chessboard[m][j];
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
m = n;
}
l--;
n = -1;
}
}
}
}
if (m != -1 && m != l) {
this->chessboard[l][j] = this->chessboard[m][j];
this->numberOfEmptyCells--;
this->clearCell(m, j);
}
}
}
void Game::doLeft() {
for (int i = 0; i < this->size; i++) {
int l = 0, m = -1, n = -1;
for (int j = 0; j < this->size; j++) {
if (this->chessboard[i][j] != 0) {
if (m == -1) {
m = j;
}
else {
if (n == -1) {
n = j;
if (this->chessboard[i][m] == this->chessboard[i][n]) {
// Merge
this->chessboard[i][l] = this->chessboard[i][m] * 2;
if (this->chessboard[i][l] == 2048) this->won = true; // Win!!!
if (m != l) {
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
this->clearCell(i, n);
// Add score
this->score += this->chessboard[i][l];
m = -1;
}
else {
// Move
if (m != l) {
this->chessboard[i][l] = this->chessboard[i][m];
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
m = n;
}
l++;
n = -1;
}
}
}
}
if (m != -1 && m != l) {
this->chessboard[i][l] = this->chessboard[i][m];
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
}
}
void Game::doRight() {
for (int i = 0; i < this->size; i++) {
int l = this->size - 1, m = -1, n = -1;
for (int j = this->size - 1; j >= 0; j--) {
if (this->chessboard[i][j] != 0) {
if (m == -1) {
m = j;
}
else {
if (n == -1) {
n = j;
if (this->chessboard[i][m] == this->chessboard[i][n]) {
// Merge
this->chessboard[i][l] = this->chessboard[i][m] * 2;
if (this->chessboard[i][l] == 2048) this->won = true; // Win!!!
if (m != l) {
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
this->clearCell(i, n);
// Add score
this->score += this->chessboard[i][l];
m = -1;
}
else {
// Move
if (m != l) {
this->chessboard[i][l] = this->chessboard[i][m];
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
m = n;
}
l--;
n = -1;
}
}
}
}
if (m != -1 && m != l) {
this->chessboard[i][l] = this->chessboard[i][m];
this->numberOfEmptyCells--;
this->clearCell(i, m);
}
}
}
void Game::restart() {
this->initChessboard(); // Init chessboard
}
void Game::stop() {
this->over = true;
}
#pragma mark -
#pragma mark Is over or won?
bool Game::isOver() const {
return this->over;
}
bool Game::isWon() const {
return this->won;
}
bool Game::isTerminated() const {
return this->over || this->won;
}
#pragma mark -
#pragma mark View
void Game::print() const {
std::cout << "================================================================" << std::endl;
std::cout << "Score: " << this->score << std::endl;
std::cout << "Empty: " << this->numberOfEmptyCells << std::endl;
if (this->isTerminated()) {
if (this->won)
std::cout << "YOU WIN!!!" << std::endl;
else if (this->over)
std::cout << "YOU LOSE!!!" << std::endl;
}
for (int i = 0; i < this->size; i++) {
for (int j = 0; j < this->size; j++) {
printf("%4d ", this->chessboard[i][j]);
}
std::cout << std::endl;
}
std::cout << "================================================================" << std::endl << std::endl;
}
void Game::printHelpInfo() const {
// 2048
std::cout << std::endl;
COUTENDL(" #### #### ## ###### ");
COUTENDL(" ########## ######## ## ### ### ");
COUTENDL(" ##### ##### ### ### ## ### ### ### ");
COUTENDL(" ### #### ### ### ## ### ### ### ");
COUTENDL(" #### ### ### ## ### ### ### ");
COUTENDL(" #### ### ### ## ### ### ### ");
COUTENDL(" #### ### ### ## ### ###### ");
COUTENDL(" #### ### ### ## ### #### #### ");
COUTENDL(" #### ### ### ### ### ### ### ");
COUTENDL(" #### ### ### ############## ### ### ");
COUTENDL(" #### ### ### ############## ### ### ");
COUTENDL(" #### ### ### ### ### ### ");
COUTENDL(" #### ### ### ### ### ### ");
COUTENDL(" ############## ######## ### ### ### ");
COUTENDL(" ############## #### ### ###### ");
std::cout << std::endl;
// Help
COUTENDL("Control:");
COUTENDL("WASD: A - Left, S - Down, W - Up, D - Right");
COUTENDL("Vim: H - Left, J - Down, K - Up, L - Right");
std::cout << std::endl;
}
void Game::draw(HDC hdc) const {
this->drawChessboard(hdc); // Draw chessboard grid
// Draw cells
for (int i = 0; i < this->size * this->size; i++) {
int row = i / this->size;
int col = i % this->size;
// if (this->chessboard[row][col] == 0) continue;
this->drawCell(hdc, row, col, this->chessboard[row][col]);
}
}
void Game::drawTopBar(HDC hdc) const {
// Draw 2048
this->drawLogoText(hdc);
// Draw score label
this->drawScoreLabel(hdc);
// Draw time rect bg
RECT timeRect = this->scoreLabelRect;
long width = getRectWidth(timeRect);
timeRect.left -= width;
timeRect.right -= width;
// Draw bg round rect
drawRoundRect(hdc, timeRect, RGB(187, 173, 160));
}
void Game::drawLogoText(HDC hdc) const {
RECT logoRect = this->topBarRect;
logoRect.right = (logoRect.left + logoRect.right) / 2;
drawNumberOnRect(hdc, logoRect, 2048, RGB(119, 110, 101));
}
void Game::drawScoreLabel(HDC hdc) const {
RECT scoreLabelRect = this->scoreLabelRect;
// Draw bg round rect
drawRoundRect(hdc, this->scoreLabelRect, RGB(187, 173, 160));
// Draw 'SCORE' text
long scoreLabelHeight = getRectHeight(scoreLabelRect);
scoreLabelRect.bottom -= (scoreLabelHeight * 0.7);
HFONT font = createFont(getRectHeight(scoreLabelRect) * 0.66);
SetTextColor(hdc, 0xeee4da); // Font color
SetBkMode(hdc, TRANSPARENT); // Background color transparentk
SelectObject(hdc, font); // Set font
DrawText(hdc, TEXT("SCORE"), -1, &scoreLabelRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DeleteObject(font);
// Draw score
scoreLabelRect.top += (scoreLabelHeight * 0.3);
scoreLabelRect.bottom += (scoreLabelHeight * 0.7);
drawNumberOnRect(hdc, scoreLabelRect, this->score, RGB(249, 246, 242));
}
void Game::drawChessboard(HDC hdc) const {
// Draw top bar
this->drawTopBar(hdc);
// Draw chessboard bg
drawRoundRect(hdc, this->chessboardRect, RGB(187, 173, 160));
}
void Game::drawCell(HDC hdc, const int& row, const int& col, const int& value) const {
long spacing = this->chessboardWidth / 4;
long cellWidth = spacing * 0.8;
long cellMargin = (spacing - cellWidth) / 2;
POINT cellTopLeftPoint = { cellMargin + this->chessboardRect.left + col * spacing, cellMargin + this->chessboardRect.top + row * spacing };
RECT cellRect = { cellTopLeftPoint.x, cellTopLeftPoint.y, cellTopLeftPoint.x + cellWidth, cellTopLeftPoint.y + cellWidth };
COLORREF bgColor;
COLORREF fontColor;
this->getCellColor(value, &fontColor, &bgColor);
// Draw cell bg
drawRoundRect(hdc, cellRect, bgColor);
if (0 == value) return;
// Draw cell number
drawNumberOnCell(hdc, cellRect, value, fontColor);
}
void Game::setRect(const RECT clientRect) {
long clientWidth = getRectWidth(clientRect);
long clientHeight = getRectHeight(clientRect);
long gameWidth, gameHeight;
if (clientWidth < clientHeight) {
gameHeight = min(clientWidth / 3 * 4, clientHeight);
gameWidth = gameHeight / 4 * 3;
}
else {
gameHeight = clientHeight;
gameWidth = gameHeight / 4 * 3;
}
this->chessboardWidth = gameWidth;
long topBarHeight = gameHeight - gameWidth;
long marginTop = (clientHeight - gameHeight) / 2;
long marginLeft = (clientWidth - gameWidth) / 2;
// Top bar rect
this->topBarRect = { marginLeft, marginTop, marginLeft + gameWidth, marginTop + topBarHeight };
// Score label rect
this->scoreLabelRect = this->topBarRect;
this->scoreLabelRect.left += (gameWidth * 3 / 4);
this->scoreLabelRect.bottom -= (getRectHeight(this->scoreLabelRect) / 2);
this->scoreLabelRect.top += (topBarHeight * 0.1);
this->scoreLabelRect.bottom += (topBarHeight * 0.1);
// Chessboard rect
this->chessboardRect = { marginLeft, marginTop + topBarHeight, marginLeft + gameWidth, marginTop + gameHeight };
}
Utils头与其源文件
// Utils.h
#pragma once
long getRectWidth(RECT rect);
long getRectHeight(RECT rect);
void drawLine(HDC hdc, int x1, int y1, int x2, int y2);
void drawRoundRect(HDC hdc, RECT cellRect, COLORREF bgColor);
void drawNumberOnCell(HDC hdc, RECT cellRect, const int& number, COLORREF fontColor);
void drawNumberOnRect(HDC hdc, RECT rect, const int& number, COLORREF fontColor);
void drawNumberOnTimeLabel(HDC hdc, RECT rect, LPCWSTR pWStr, COLORREF fontColor);
HFONT createFont(int height);
void getTimeStringByValue(UINT time, LPCWSTR result);
// Utils.cpp
#include "framework.h"
#include <cmath>
long getRectWidth(RECT rect) {
return rect.right - rect.left;
}
long getRectHeight(RECT rect) {
return rect.bottom - rect.top;
}
// 画线
void drawLine(HDC hdc, int x1, int y1, int x2, int y2) {
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
}
// 画圆角矩形
void drawRoundRect(HDC hdc, RECT cellRect, COLORREF bgColor) {
HBRUSH brush = CreateSolidBrush(bgColor);
HPEN pen = CreatePen(PS_NULL, 0, NULL);
SelectObject(hdc, brush);
SelectObject(hdc, pen);
RoundRect(hdc, cellRect.left, cellRect.top, cellRect.right, cellRect.bottom, 10, 10);
DeleteObject(brush);
DeleteObject(pen);
}
// 创建固定字体,高度不同
HFONT createFont(int height) {
return CreateFont(height, 0, 0, 0, FW_BLACK, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, FF_ROMAN, TEXT("Arial"));
}
// 将时间(单位:百分之一秒)转换成宽字符串(长度为8位)
void getTimeStringByValue(UINT time, LPCWSTR result) {
// int hSeconds = time % 100;
time /= 100;
int seconds = time % 60;
time /= 60;
int minutes = time % 60;
WCHAR buffer[10];
wsprintfW(buffer, L"%02d:%02d", minutes, seconds);
//if (seconds < 10) {
// buffer[5] = buffer[4];
// buffer[4] = buffer[3];
// buffer[3] = L'0';
//}
memcpy((void*)result, buffer, sizeof(buffer));
}
// 画数字
void drawNumberOnCell(HDC hdc, RECT cellRect, const int& number, COLORREF fontColor) {
int fontSize = log2(number);
double factor = fontSize < 7 ? 1 : (fontSize < 10 ? 1.5 : 2);
HFONT font = createFont(getRectHeight(cellRect) * 0.66 / factor);
SetTextColor(hdc, fontColor); // Font color
SetBkMode(hdc, TRANSPARENT); // Background color transparentk
SelectObject(hdc, font); // Set font
wchar_t buffer[256];
wsprintfW(buffer, L"%d", number);
DrawText(hdc, buffer, -1, &cellRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DeleteObject(font);
}
void drawNumberOnRect(HDC hdc, RECT rect, const int& number, COLORREF fontColor) {
HFONT font = createFont(getRectHeight(rect) * 0.66);
SetTextColor(hdc, fontColor); // Font color
SetBkMode(hdc, TRANSPARENT); // Background color transparentk
SelectObject(hdc, font); // Set font
wchar_t buffer[256];
wsprintfW(buffer, L"%d", number);
DrawText(hdc, buffer, -1, &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DeleteObject(font);
}
void drawNumberOnTimeLabel(HDC hdc, RECT rect, LPCWSTR pWStr, COLORREF fontColor) {
HFONT font = createFont(getRectHeight(rect) * 0.66 / 2);
SetTextColor(hdc, fontColor); // Font color
SetBkMode(hdc, TRANSPARENT); // Background color transparentk
SelectObject(hdc, font); // Set font
DrawText(hdc, pWStr, -1, &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
DeleteObject(font);
}
资料引用与出处
- 游戏开发素材 | indienova 独立游戏
- 奔跑骚年 - 简书
- Windows 窗体控件 | Microsoft Docs
- 章节概述 - 2D 游戏开发教程
- 精通Win32 API高级界面编程 - 网易云课堂
- 创建窗口 - Windows应用程序| Microsoft Docs
- Windows程序设计 - 标签 - wid - 博客园
- [因为我不懂啊]-什么是状态机编程(设计模式)(0) - 简书
- 游戏开发之状态机的实现与优化-GameRes游资网
- Cocos2d-X手游开发课程【实用技能,学完即飞升脱离饿鬼界】_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
C语言课程设计的更多相关文章
- 学生管理系统-火车订票系统 c语言课程设计
概要: C 语言课程设计一---学生管理系统 使使用 C 语言实现学生管理系统.系统实现对学生的基本信息和考试成绩的 管理.采用终端命令界面,作为系统的输入输出界面.采用文件作为信息存储介质. 功能描 ...
- C语言课程设计—图书管理系统
这是本人大一第二学期初C语言课程设计的作品,嘿嘿,本来以为已经找不到原稿了,今天无意中居然在QQ网络硬盘中找到了当初的teta版,公布于此,以作纪念. C源码例如以下: #include<std ...
- Java语言课程设计——博客作业教学数据分析系统(201521123107 张翔)
#Java语言课程设计--博客作业教学数据分析系统(个人博客) 1.团队课程设计博客链接 [博客作业教学数据分析系统(From:网络五条狗)](http://www.cnblogs.com/fanta ...
- C语言课程设计(成绩管理系统)
C语言课程设计(成绩管理系统) 翻到了大学写的C语言课程设计,缅怀一下 内容: 增加学生成绩 查询学生成绩 删除 按照学生成绩进行排序 等 #include <stdio.h> #incl ...
- 大一C语言课程设计——班级档案管理系统
记录我在大一第二学期期末做的C语言课程毕业设计 1. 班级档案管理系统运用到的主要结构体 typedef struct birthday //出生日期{int year;int month;int d ...
- C语言课程设计大整数运算
该大整数运算系统用于对有符号的位数不超过500位的大整数进行加.减.乘.除四则运算和计算N(0<=N<=10000)的阶乘.注意事项 : 1.操作期间,进行四则运算时若大整数为正数请 ...
- C语言课程设计——电影院订票系统
1. 课题简介 大家都爱看电影,现请参考一个熟悉电影票预订系统,实现C语言版的订票系统.了解订票如何实现的.系统主要有2类用户:管理员用户和顾客用户. 管理员用户登录系统后,实现电影放映厅信息管理和电 ...
- C语言课程设计 Win32应用程序
问题描述: 请设计一个职工信息管理程序,以方便人事部门对本单位职工的管理,该程序应该具有以下功 能: (1)能从键盘输入职工的信息 . (2)给定职工号,显示职工的信息. (3)给定工作部门,显示该部 ...
- 第一次 C语言课程设计
小学生测验 最近比较忙,就拿前几天做的课设项目水一水. 内容 面向小学1~2年级学生,随机选择两个整数和加减法形成算式要求学生解答. 功能要求: (1)电脑随机出10道题,每题10分,程序结束时显示学 ...
随机推荐
- [USACO17JAN] 晋升者计数 dfs序+树状数组
[USACO17JAN] 晋升者计数 dfs序+树状数组 题面 洛谷P3605 题意:一棵有点权的树,找出树中所有\((u,v)\)的对数,其中\(u,v\)满足\(val(u)\le val(v)\ ...
- Cogs 1695. 梦游仙境(分块)
梦游仙境 ★☆ 输入文件:XTTMYXJ.in 输出文件:XTTMYXJ.out 简单对比 时间限制:5 s 内存限制:512 MB [题目描述] 在Asm.def仍然在与人工智能进行艰苦的斗争时,雪 ...
- sequence_loss的解释
在做seq2seq的时候,经常需要使用sequence_loss这是损失函数. 现在分析一下sequence_loss这个函数到底在做什么 # coding: utf-8 import numpy a ...
- seq2seq聊天模型(二)——Scheduled Sampling
使用典型seq2seq模型,得到的结果欠佳,怎么解决 结果欠佳原因在这里 在训练阶段的decoder,是将目标样本["吃","兰州","拉面" ...
- Luogu5339 [TJOI2019]唱、跳、rap和篮球 【生成函数,NTT】
当时看到这道题的时候我的脑子可能是这样的: My left brain has nothing right, and my right brain has nothing left. 总之,看到&qu ...
- Simple Problem with Integers(POJ 3486)
A Simple Problem with Integers Time Li ...
- 查询Linux下文件格式.
备忘 file 命令可以查一个文件的格式 readelf -h 可执行文件名. 可以查询可执行文件的详细的格式 向Windows中exeinfo 软件类
- Nginx中配置非英文域名
前两天遇到个配置越南语的域名的情况.域名和ip解析完成后,直接ping域名也不通,还以为是解析问题.研究了半天,nginx配置非英文域名时,需要有其他操作. 非英文域名转换成punycode编码才可以 ...
- 【面试题总结】1、统计字符串中某个单词出现的次数(1-C++实现)
[解决方法一]C++ map解决 一.map中的find函数: 用于查找map中是否包含某个关键字条目,传入的参数是要查找的key,最后返回一个迭代器,如果没有找到,则返回的迭代器等于end()返回的 ...
- 【git 命令总结】一
git help config init commit diff rename mv rm head revert reset branch checkout branch-diff fast-for ...