傅小森的游戏制作之路-别踩白块儿

项目前言

别踩白块儿 这… 还用开发吗?

别人已经制作了呀

触屏版本多的是

是挺多的 但是 你见过按键版本的嘛?

没见过吧 这就是需要开发出来 ,

进入项目需求环节

项目需求

别踩白块儿 顾名思义:不要踩白色方块 踩了你就失败 到达一定的分数或者到达一定的时间会加快速度 直到踩了白块就认定失败

项目分析

开发语言:C/C++ (优选C++)

  1. 初始化游戏场景
  2. 设计游戏者类
  3. 绘制游戏场景
  4. 按键控制
  5. 绘制失败场景

项目实现

实现效果

在这里插入图片描述

别踩白块儿 主框架类声明

#pragma once
#undef UNICODE
#undef _UNICODE

#include"Games.h"
const  int GameInterfaceWidth = 640;
const  int GameInterfaceHeight = 480;

using GameState = enum class DontTapTheWhiteTileState;
//别踩白块儿
class DontTapTheWhiteTile{

	
public:
	DontTapTheWhiteTile();

	//初始化属性
	void initAttribute(Games* Gamesfirst, Games* GamesSecond);
	
	//运行游戏
	void RunGame(void);

	~DontTapTheWhiteTile();
private:

	//初始化游戏场景
	void initGameInterface();

	//初始化游戏
	void initGeme();

	//绘制游戏界面外框
	void DrawOuterFrame();

	//绘制游戏界面
	void DrawGameInterfaces();

	//处理游戏按键
	void HandlesPlayerButtons(const char& key);
		
	//延时
	void TimeDelay();



	Games* m_GamesFirst;
	Games* m_GamesSecond;

	GameState m_GamesFirstState;
	GameState m_GamesSecondState;
};
//绘制游戏场景
class DrawGameInterface {

public:
	DrawGameInterface( Games &theGame,  GameState& gameState);
	void operator()();
private:
	GameState&m_GameState;
	Games& m_theGame;
};
	
class OutText {
public:
	OutText(Pos& Pos,  int x ,int y ,const char &c);
	
	void operator()();
private:
	Pos& m_Pos;
	Pos m_pos;
	const char &m_C;
};

class OutTextByStr {
public:
	OutTextByStr(Pos& Pos, int x, int y, const char *str);

	void operator()();
private:
	Pos& m_Pos;
	Pos m_pos;
	const char *m_Str;
};
/ 绘制矩形
class DrawRectangle {

public:
	DrawRectangle(Pos& Pos, int x1, int y1, int x2, int y2);
	
	void operator()();

private:
	Pos& m_Pos;
	Pos m_pos[2];
};

/// 绘制有边框矩形
class DrawFillRectangle {

public:
	DrawFillRectangle(Pos& Pos, int x1, int y1, int x2, int y2);

	void operator()();

private:
	Pos& m_Pos;
	Pos m_pos[2];
}; 

// 绘制有边框矩形
class DrawSolidRectangle {

public:
	DrawSolidRectangle(Pos& Pos, int x1, int y1, int x2, int y2);

	void operator()();

private:
	Pos& m_Pos;
	Pos m_pos[2];
};

//绘制游戏界面外框
class  DrawOuterFrames {
public:
	DrawOuterFrames(Games& Games);
	void operator()();
private:
	Games& m_Games;
};

//处理游戏者按键
class HandlesPlayerButton {
public:
	HandlesPlayerButton(GameState& GameState,  Games& theGame, const char& Key);
	void operator()();
private:
	GameState& m_GameState;
	Games& m_theGame;
	const char& m_key;
 };

class GameReset {

public:
	GameReset(GameState& GameState, Games& theGame);
	void operator()();
private:
	GameState& m_GameState;
	Games& m_theGame;
};

//绘制游戏界面中的一行任务
class DrawRowTask {
public:
	DrawRowTask(int& baseY, int& TaskId, Games& Games);
	void operator()();
	
private:
	int& m_baseY;
	int& m_TaskId;
	Games& m_theGame;

	static const size_t ColorSize = 4;
	static const COLORREF LineColor = 0xe9dbd6;
	static const COLORREF TextColor = WHITE;
	static const char* const start;
	int Textwidth;
	int TextHeight;
};

// 绘制通过游戏后的界面
class DrawPass {
public:
	DrawPass(Games& Games);
	void operator()();
private:
	Games& m_theGame;
	static const COLORREF FillColor = GREEN;
	static const int TextY = 100;
	static const COLORREF TextColor = WHITE;
};
//绘制失败界面
class DrawFail {

public:
	DrawFail(Games& theGame, GameState& GameState);
	void operator()();
private:
	Games& m_theGame;
	GameState& m_GameState;
	static const int zero = 0;
};
//延时
class TimeDelayS {
public:
	TimeDelayS(int &ms);

	void operator()();

	static clock_t oldclock;
	int &m_ms;
};

别踩白块儿 主框架类实现

#include "DontTapTheWhiteTile.h"
#include"Games.h"
#include<iostream>
#include<graphics.h>
#include<conio.h>


DontTapTheWhiteTile::DontTapTheWhiteTile(){
	initGameInterface();

}

void DontTapTheWhiteTile::initAttribute(Games* Gamesfirst, Games* GamesSecond){
	this->m_GamesFirst= Gamesfirst;
	this->m_GamesSecond = GamesSecond;
	initGeme();
}


void DontTapTheWhiteTile::RunGame(void){
	const char ESc =27;
	char ch = 0;
	while (ch!=ESc){

		while (_kbhit()){
			ch = _getch();
			HandlesPlayerButtons(ch);
		}
		DrawGameInterfaces();
		TimeDelay();
	}
}

DontTapTheWhiteTile::~DontTapTheWhiteTile(){
	
	closegraph();
}

void DontTapTheWhiteTile::initGameInterface(){
	
	initgraph(GameInterfaceWidth, GameInterfaceHeight);
	const DWORD bkcolor = (DWORD)0x01bbfb;
	srand((unsigned int)time(nullptr));
	setbkcolor(bkcolor);
	cleardevice();
}

void DontTapTheWhiteTile::initGeme(){
	
	this->m_GamesFirstState = GameState::GameStart;
	this->m_GamesSecondState = GameState::GameStart;
	this->DrawOuterFrame();

}

void DontTapTheWhiteTile::DrawOuterFrame(){

	DrawOuterFrames(*this->m_GamesFirst)();
	DrawOuterFrames(*this->m_GamesSecond)();

}

void DontTapTheWhiteTile::DrawGameInterfaces(){

	DrawGameInterface(*this->m_GamesFirst, this->m_GamesFirstState)();

	DrawGameInterface(*this->m_GamesSecond, this->m_GamesSecondState)();

}

void DontTapTheWhiteTile::HandlesPlayerButtons(const char &key){

	HandlesPlayerButton(this->m_GamesFirstState, *this->m_GamesFirst, key)();

	HandlesPlayerButton(this->m_GamesSecondState, *this->m_GamesSecond, key)();

}

void DontTapTheWhiteTile::TimeDelay(){
	static int Ms = 16;
	TimeDelayS t(Ms);

	t();
}

DrawGameInterface::DrawGameInterface( Games& theGame, GameState& gameState) :  m_theGame(theGame), m_GameState(gameState) {
	
}

void DrawGameInterface::operator()() {

	GameState& GameState = m_GameState;
	Games& theGame = m_theGame;
	int& nextTaskY = m_theGame.m_nextTaskY;
	Task& TaskId = m_theGame.m_TaskId;


	DrawPass DrawPass(theGame);
	DrawFail DrawFail(theGame, GameState);
	switch (GameState){
	case GameState::GamePassani: {
		if (nextTaskY == 100) {
			GameState = GameState::GamePass;
			DrawPass();
			break;
		}
	}

	case GameState::GameStart:
	case GameState::GameRun: {

		if (nextTaskY == 100) {
			return;
		}

		nextTaskY -= (nextTaskY - 100 + 9) / 10;

		int  rowy = nextTaskY;
		int  TaskIt = TaskId;
		DrawRowTask DrawRowTask(rowy, TaskIt, theGame);
		do{
			rowy -= 100;
			--TaskIt;
			DrawRowTask();
		} while (rowy > 0);
		
		rowy = nextTaskY;
		TaskIt = TaskId;

		do {
			
			DrawRowTask();
			rowy += 100;
			++TaskIt;
		} while (rowy < 400);

		break;
	}

	case GameState::GameFailani:
		DrawFail();
		break;

	case  GameState::GamePass:
	case GameState::GameFAIL:
		break;
	
	}
}


OutText::OutText(Pos& Pos, int x, int y, const char &c) :m_Pos(Pos), m_pos{ x,y }, m_C(c){
}

void OutText::operator()(){

	int&& x = this->m_Pos.x + this->m_pos.x;
	int&& y = this->m_Pos.y + this->m_pos.y;

	outtextxy(x, y, m_C);
}

OutTextByStr::OutTextByStr(Pos& Pos, int x, int y, const char* str):m_Pos(Pos), m_pos{ x,y }, m_Str(str){

}

void OutTextByStr::operator()(){

	int&& x = this->m_Pos.x + this->m_pos.x;
	int&& y = this->m_Pos.y + this->m_pos.y;

	outtextxy(x, y, m_Str);
}

DrawRectangle::DrawRectangle(Pos& Pos, int x1, int y1, int x2, int y2) :m_Pos(Pos), m_pos{ {x1,y1},{x2,y2} }{

}

void DrawRectangle::operator()(){
	int &&left = this->m_Pos.x + this->m_pos[false].x;
	int&& top = this->m_Pos.y + this->m_pos[false].y;
	int &&right = this->m_Pos.x + this->m_pos[true].x; 
	int &&bottom = this->m_Pos.y + this->m_pos[true].y;

	rectangle(left, top, right, bottom);
}			  

DrawFillRectangle::DrawFillRectangle(Pos& Pos, int x1, int y1, int x2, int y2) :m_Pos(Pos), m_pos{ {x1,y1},{x2,y2} }{

}

void DrawFillRectangle::operator()() {
	int&& left = this->m_Pos.x + this->m_pos[false].x;
	int&& top = this->m_Pos.y + this->m_pos[false].y;
	int&& right = this->m_Pos.x + this->m_pos[true].x;
	int&& bottom = this->m_Pos.y + this->m_pos[true].y;

	fillrectangle(left, top, right, bottom);
}

DrawSolidRectangle::DrawSolidRectangle(Pos& Pos, int x1, int y1, int x2, int y2) :m_Pos(Pos), m_pos{ {x1,y1},{x2,y2} }{

}

void DrawSolidRectangle::operator()() {
	int&& left = this->m_Pos.x + this->m_pos[false].x;
	int&& top = this->m_Pos.y + this->m_pos[false].y;
	int&& right = this->m_Pos.x + this->m_pos[true].x;
	int&& bottom = this->m_Pos.y + this->m_pos[true].y;

	fillrectangle(left, top, right, bottom);
}

DrawOuterFrames::DrawOuterFrames(Games& Games):m_Games(Games){
	
}

void DrawOuterFrames::operator()(){

	const DWORD linecolor = 0xfb9700;
	setlinecolor(linecolor);

	auto& pos = this->m_Games.m_pos;

	DrawRectangle(pos, 0, 0, 243, 464)();
	const DWORD fillcolor = 0xeca549;
	setfillcolor(fillcolor);

	settextcolor(BLACK);
	settextstyle(16, 0, "Verdana");
	setbkmode(TRANSPARENT);
	
	DrawSolidRectangle(pos, 2, 2, 241, 21)();

	int TextWidth = textwidth(this->m_Games.m_Name);
	int CalculateTextX  = (244 - TextWidth) / 2;
	OutTextByStr(pos, CalculateTextX, 4, this->m_Games.m_Name)();

	DrawSolidRectangle(pos, 2, 23, 241, 42)();
	char temp[150];
	sprintf_s(temp, "最好记录:%.3f 秒", this->m_Games.m_bestTime);
	OutTextByStr(pos, 10, 26, temp)();

	DrawSolidRectangle(pos, 2, 445, 241, 462)();
	for (size_t i = 0; i < 4; i++){
		OutText(pos, 2 + i * 60 + 26, 446, this->m_Games.m_key[i])();
	}
}

HandlesPlayerButton::HandlesPlayerButton(GameState& GameState,  Games& theGame, const char& Key):m_GameState(GameState),m_theGame(theGame), m_key(Key){

}

void HandlesPlayerButton::operator()(){
	
	Games& theGame = this->m_theGame;
	const char*& Keys = theGame.m_key;
	const char& Key = this->m_key;
	GameState &GameState = this->m_GameState;
	auto& Task = theGame.m_Task;
	auto& TaskId = theGame.m_TaskId;;
	auto& nextTaskY = theGame.m_nextTaskY;
	auto& bestTime = theGame.m_bestTime;
	auto& lastTime = theGame.m_lastTime;
	auto& beginClock = theGame.m_beginClock;
	auto& failErrorKey = theGame.m_failErrorKey;

	bool flag;
	switch (GameState){

	case GameState::GameStart:
		 flag = strchr(Keys, Key);
		if (flag){
			beginClock = clock();
			GameState = GameState::GameRun;

		}
	case  GameState::GameRun: {
	
		const char* dest = strchr(Keys, Key);
		flag = dest;
		if (flag) {
			byte pos = dest - Keys;
			if (pos == Task[TaskId]) {
				++TaskId;
				nextTaskY += 100;

				if (TaskId == MaxTask) {
					clock_t&& c = clock();
					lastTime = ((float)(clock() - beginClock)) / CLOCKS_PER_SEC;
					if (lastTime < bestTime) {
						bestTime = lastTime;
					}
					TaskId++;
					nextTaskY += 100;
					GameState = GameState::GamePassani;

				}
			}else{
				failErrorKey = pos;
				GameState = GameState::GameFailani;
			}
		}
		
		break;

	}


	case  GameState::GamePassani:
	case  GameState::GameFailani:
		break;
	case GameState::GameFAIL:
			flag = strchr(Keys, Key);
		if (flag){		
			GameReset(GameState, theGame)();			
		}


	default:
		break;
	}

}

GameReset::GameReset(GameState& GameState, Games& theGame):m_GameState(GameState),m_theGame(theGame){

}

void GameReset::operator()(){
	Games& theGame = this->m_theGame;
	GameState& GameState = this->m_GameState;
	GameState = GameState::GameStart;
	theGame.init();
	DrawOuterFrames DrawOuterFrame(theGame);
	DrawOuterFrame();
}

DrawRowTask::DrawRowTask(int& baseY, int& TaskId, Games& Games):m_baseY(baseY),m_TaskId(TaskId),m_theGame(Games){
	 Textwidth = textwidth(start);
	 TextHeight = textheight(start);
}

const char* const DrawRowTask::start = "开始";


void DrawRowTask::operator()(){

	int First = this->m_baseY;
	int&& Second = First + 99;
	if (First < 0) {
		First = 0;
	}
	if (Second > 399) {
		Second = 399;
	}

	auto & GamesTaskId = this->m_theGame.m_TaskId;
	int& TaskId = this->m_TaskId;
	const auto& theGameTask = this->m_theGame.m_Task;
	auto& theGamePos = this->m_theGame.m_pos;


	COLORREF color[ColorSize];
	if (TaskId < 0) {
		for (size_t i = 0; i < ColorSize; i++){
			color[i] = YELLOW;
		}
	}else if (TaskId > MaxTask) {
		for (size_t i = 0; i < ColorSize; i++) {
			color[i] = GREEN;
		}
	}else {
		for (size_t i = 0; i < ColorSize; i++){
			color[i] = WHITE;
		}
		
		color[theGameTask[TaskId]] = (TaskId < GamesTaskId) ? LIGHTGRAY : BLACK;

	}

	
	setlinecolor(LineColor);

	for (size_t i = 0; i < ColorSize; i++) {
		setfillcolor(color[i]);
		DrawFillRectangle(theGamePos, 2 + i * 60, 44 + 399 - First, 2 + i * 60 + 59, 44 + 399 - Second)();
	
	}
	if (TaskId==0 && GamesTaskId==0){
		
		int TextX = 2 + theGameTask[TaskId] * 60 + (60 - Textwidth) / 2;
		int TextY = 44 + 399 - 99 - First + (100 - TextHeight) / 2;

		settextcolor(TextColor);
		settextstyle(16, 0, "Verdana");
		OutTextByStr(theGamePos, TextX, TextY, start)();

	}
}

DrawPass::DrawPass(Games& Games):m_theGame(Games){

}

void DrawPass::operator()(){
	
	auto& theGamePos = this->m_theGame.m_pos;
	auto& theGamelastTime = this->m_theGame.m_lastTime;
	setfillcolor(FillColor);
	DrawSolidRectangle(theGamePos, 2, 44, 241, 443)();

	settextcolor(TextColor);
	settextstyle(16, 0, "Verdana");

	const char OK[] = "成功";
	int Textwidth = textwidth(OK);
	int TextX = (244 - Textwidth) / 2;
	OutTextByStr(theGamePos, TextX, TextY, OK)();

	char temp[100];
	sprintf_s(temp, "成绩:%.3f 秒", theGamelastTime);

	Textwidth = textwidth(temp);
	TextX = (244 - Textwidth) / 2;
	OutTextByStr(theGamePos, TextX, 200, temp)();

	sprintf_s(temp, "速度:%.3f 秒", MaxTask / theGamelastTime);

	Textwidth = textwidth(temp);
	TextX = (244 - Textwidth) / 2;
	OutTextByStr(theGamePos, TextX, 200, temp)();

	const char (hint)[21] = "按任意控制键重新开始";
	settextstyle(16, 0, "Verdana");
	Textwidth = textwidth(hint);
	TextX = (244 - Textwidth) / 2;
	OutTextByStr(theGamePos, TextX, 400, hint)();

}

DrawFail::DrawFail(Games& theGame, GameState& GameState):m_theGame(theGame) , m_GameState(GameState) {

}

void DrawFail::operator()(){

	auto& theGame = this->m_theGame;
	auto& theGameFailFrame = theGame.m_failFrame;
	auto& theGameFailRect = theGame.m_failRect;
	const int counts = MaxTask + 10;
	auto& theGamePos = theGame.m_pos;

	auto& theGameFailRectLeft = theGameFailRect.left;
	auto& theGameFailRectRight = theGameFailRect.right;
	auto& theGameFailRectTop = theGameFailRect.top;
	auto& theGameFailRectBottom = theGameFailRect.bottom;

	if (theGameFailFrame == zero){
		auto& theGameFailkeys = theGame.m_failErrorKey;
		auto& theGameNextTaskY = theGame.m_nextTaskY;
	

		theGameFailRectLeft = 3 + theGameFailkeys * 60;
		theGameFailRectRight = theGameFailRect.left + 57;
		theGameFailRectBottom = theGameNextTaskY + 1;
		theGameFailRectTop = theGameNextTaskY + 98;
		if (theGameFailRectTop > 398) {
			theGameFailRectTop = 398;
		}
		theGameFailRectBottom = 44 + 398 - theGameFailRectBottom;
		theGameFailRectTop = 44 + 398 - theGameFailRectTop;

	}
	
	if (theGameFailFrame < counts) {
		const auto fillcolor = ((theGameFailFrame / 6) % 2 == 0) ? RED : LIGHTRED;
		setfillcolor(fillcolor);
		DrawSolidRectangle(theGamePos, theGameFailRectLeft, theGameFailRectBottom, theGameFailRectRight, theGameFailRectTop)();
		++theGameFailFrame;

	}else {
		auto& theGameState = this->m_GameState;
		theGameState = GameState::GameFAIL;
		const auto fillcolor = RED;
		auto& theGamembestTime = theGame.m_bestTime;

		setfillcolor(fillcolor);
		DrawSolidRectangle(theGamePos, 2, 44, 241, 443)();

		const auto textcolor = WHITE;
		settextcolor(textcolor);
		settextstyle(60, 0, "Verdana");

		const char *No = "失败";
		int Textwidth = textwidth(No);
		int TextX = (244 - Textwidth) / 2;
		int TextY = 100;
	
		OutTextByStr(theGamePos, TextX, TextY, No)();
		
		settextstyle(20, 0, "Verdana");
		char temp[100];
		sprintf_s(temp, "历史最好成绩:%.3f 秒", theGamembestTime);

		Textwidth = textwidth(temp);
		TextX = (244 - Textwidth) / 2;
		TextY = 200;
		OutTextByStr(theGamePos, TextX, TextY, temp)();

		const char(hint)[21] = "按任意控制键重新开始";
		settextstyle(16, 0, "Verdana");
		Textwidth = textwidth(hint);
		TextX = (244 - Textwidth) / 2;
		TextY = 400;
		OutTextByStr(theGamePos, TextX, TextY, hint)();
	}

}

clock_t TimeDelayS::oldclock = clock();

TimeDelayS::TimeDelayS(int& ms):m_ms(ms)
{
}

void TimeDelayS::operator()(){
	
	oldclock += this->m_ms * CLOCKS_PER_SEC / 1000;
	if (clock() > oldclock){
		oldclock = clock();
	}else {
		while (clock()< oldclock){
			Sleep(1);
		}
	}
}

main.cpp 使用别踩白块儿主框架类游戏者类

#include"DontTapTheWhiteTile.h"
#include"Games.h"

int main(void) {
	DontTapTheWhiteTile d;

	Games game1("罗小黑", "asdf", { 38,8 });
	Games game2("罗小白", "jkl;", { 358,8 });
	d.initAttribute(&game1, &game2);
	d.RunGame();
	return 0;
}

游戏者类声明

#pragma once
#include<graphics.h>
#include<iostream>
#include"DontTapTheWhiteTileState.h"

using Task = unsigned char;
const size_t  MaxTask = 50;
const int DefaultNextTaskY = 200;

struct Pos{
	int x;
	int y;
};

class Games {
	Games();
public:

	Games(const char* name, const char* key, Pos Pos);

	void  init();

	const char* m_Name;
	const char* m_key;

	Pos		m_pos;

	Task	m_Task[MaxTask];

	Task	m_TaskId;

	int		m_nextTaskY;

	clock_t	m_beginClock;				// 游戏开始的时钟计数
	float	m_bestTime;					// 最佳纪录的完成时间
	float	m_lastTime;					// 最后一次的完成时间

	byte	m_failErrorKey;				// 按错的键的序号(值为 0、1、2、3)

	RECT	m_failRect;					// 按错的键的区域

	int		m_failFrame;				// 失败后的动画的帧计数

};

游戏者类实现

#include "Games.h"
#include<iostream>


Games::Games(){
	
	init();
}

Games::Games(const char* name, const char* key, Pos Pos):Games(){


	this->m_Name = name;
		
	this->m_key = key;
	this->m_pos = Pos;


}

void Games::init(){
	for (Task& value : this->m_Task) {
		const int RandNum = rand();
		value = (RandNum % 4);
	}

	this->m_TaskId = 0;
	this->m_nextTaskY = DefaultNextTaskY;
	this->m_failFrame = 0;
	this->m_bestTime = 99.0f;
	
	this->m_failRect = {};
}

游戏状态枚举类

#pragma once

using GameState = enum class DontTapTheWhiteTileState;
//别踩白块儿 游戏状态
enum class DontTapTheWhiteTileState {
	GameStart,    //游戏开始状态
	GameRun,	  //游戏运行状态
	GamePassani,  //游戏通过的动画状态
	GamePass,	  //游戏通过状态
	GameFailani,  //游戏失败的动画状态
	GameFAIL	  //游戏失败状态
};

项目总结

类的封装能力,仿函数的实际应用 图形库接口了解

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小森程序员

若能帮助到你,小费自愿付费

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值