SDL2 游戏开发日记(三) 俄罗斯方块

SDL2 游戏开发日记(三)

俄罗斯方块

方块的表示

用二维数组表示方块。

#pragma once
#include <cstdlib>
#include <ctime>
#include <cassert>

using namespace std;

#define theTetrisArray TetrisArray::Instance()

struct Tetris{
	int M[4][4];
};

class TetrisArray{
private:
	Tetris mTetriss[8];
	TetrisArray(const TetrisArray &){}
	void operator=(const TetrisArray &){}	
	TetrisArray();
public:
	~TetrisArray(){

	}
	static TetrisArray & Instance(){
		static TetrisArray instance;
		return instance;
	}
	//随机生成方块
	Tetris GetArray(){
		int index = rand() % 8;
		return mTetriss[index];
	}
	int GetRandomValue(int min, int max){	
		assert(max != 0);
		int v = rand() % max + min;
		return v;
	}
};

//TetrisArray.cpp
#include "stdafx.h"
#include "TetrisArray.h"
#include <string>
TetrisArray::TetrisArray(){
	srand((unsigned int)time(0));
	//
	Tetris t[8] = {
		{
			0, 0, 0, 0,
			1, 1, 1, 1,
			0, 0, 0, 0,
			0, 0, 0, 0
		},
		{
			0, 0, 0, 0,
			0, 0, 1, 1,
			0, 0, 0, 1,
			0, 0, 0, 1
		},
		{
			0, 0, 0, 0,
			0, 1, 1, 0,
			0, 0, 1, 1,
			0, 0, 0, 0
		},
		{
			0, 0, 0, 0,
			0, 0, 1, 1,
			0, 1, 1, 0,
			0, 0, 0, 0
		},
		{
			0, 0, 0, 0,
			0, 0, 1, 1,
			0, 0, 1, 0,
			0, 0, 1, 0
		},
		{
			0, 0, 0, 0,
			0, 0, 1, 0,
			0, 1, 1, 1,
			0, 0, 0, 0
		},
		{
			0, 0, 0, 0,
			0, 1, 1, 1,
			0, 1, 0, 1,
			0, 0, 0, 0
		},
		{
			0, 0, 0, 0,
			0, 1, 1, 0,
			0, 1, 1, 0,
			0, 0, 0, 0
		}
	};	
	for (int i = 0; i < 8; i++){
		mTetriss[i] = t[i];
	}
}

方块的渲染

根据4x4的数组去绘制对应的方块

#pragma once 
#include "Renderable.h"
#include "TetrisArray.h"

class TetrisRenderable : public Renderable {
private :
	Tetris mRenderTetris;		
public :
	TetrisRenderable(){
		
	}
	~TetrisRenderable(){

	}	
	virtual void Render();
	virtual void SetPos(int x, int y){
		if (mRenderRect != NULL){
			mRenderRect->x = x;
			mRenderRect->y = y;
			mRenderRect->w = mTextureWidth * 4;
			mRenderRect->h = mTextureHeight * 4;
		}
	}
	void SetArray(Tetris Tetris){		
		for (int i = 0; i < 4; i++){
			for (int j = 0; j < 4; j++){		
				if (Tetris.M[i][j] != 1)
					Tetris.M[i][j] = 0;
				mRenderTetris.M[i][j] = Tetris.M[i][j];
			}
		}		
	}
	//方块旋转
	Tetris GetRotateTetris(int &, int &);
	//获取数组
	Tetris GetRenderTetris(int &,int &);
};


void TetrisRenderable::Render(){
	if (mIsVisible){
		int left = -1, top = -1, right = -1, bottom = -1;
		for (int i = 0; i < 4; i++){
			for (int j = 0; j < 4; j++){
				if (mRenderTetris.M[i][j] > 0){
					//获取渲染矩形
					if (left == -1)
						left = j;
					else if (left > j)
						left = j;
					if (top == -1)
						top = i;
					else if (top > i)
						top = i;
					if (right == -1)
						right = j;
					else if (right < j)
						right = j;
					if (bottom == -1)
						bottom = i;
					else if (bottom < i)
						bottom = i;					
				}
			}
		}
		for (int i = top; i < 4; i++){
			for (int j = left; j < 4; j++){
				if (mRenderTetris.M[i][j] > 0){
					SDL_Rect rect = { mRenderRect->x + (j - left) * mTextureWidth, mRenderRect->y + (i-top) * mTextureHeight,
						mTextureWidth, mTextureHeight };
					SDL_RenderCopyEx(theGame.getRenderer(), mTexture, mClipRect, &rect, 0, NULL, SDL_FLIP_NONE);
				}
			}
		}
		
	}
}

主界面

用一个15 * 21的数组表示整个区域,对应的值如果为1,绘制方块。

#pragma once

#include "Renderable.h"
#include <cassert>
#include "TetrisArray.h"
#include "TetrisRenderable.h"
#include <vector>
using namespace std;

class TetrisWorldRenderable : public Renderable{
private :
	vector<int> *mRenderArray;
	int mArrayWidth, mArrayHeight,mArrayLength;	
	//可移动方块的坐标
	int mMoveTetrisX, mMoveTetrisY;
public:
	TetrisWorldRenderable(int w, int h) :Renderable(){
		mRenderArray = new vector<int>(w*h);
		mArrayWidth = w;
		mArrayHeight = h;
		mArrayLength = w * h;
		ResetArray();			
	}

	virtual ~TetrisWorldRenderable(){
		delete mRenderArray;
	}
	virtual void Render();
	virtual void SetPos(int x,int y){
		if (mRenderRect != NULL){
			mRenderRect->x = x;
			mRenderRect->y = y;
			mRenderRect->w = mTextureWidth * mArrayWidth;
			mRenderRect->h = mTextureHeight * mArrayHeight;
		}
	}
	virtual void Update(float time_step);
	//重新开始
	void ResetArray(){
		for (int i = 0; i < mArrayLength; i++){			
			(*mRenderArray)[i] = 0;			
		}
		//初始化在中间
		SetTetrisStartPos();
	}
	//重置可控制的方块的位置
	void SetTetrisStartPos(){
		mMoveTetrisX = 6;
		mMoveTetrisY = 0;
	}
	int GetWorldWidth(){
		return mTextureWidth * mArrayWidth;
	}

	int GetWorldHeight(){
		return mTextureHeight * mArrayHeight;
	}
	//判断是否可以移动
	bool IsCanMove(int x, int y,TetrisRenderable *Tetris);
	
	bool Rotate(TetrisRenderable *Tetris);
	
	bool MoveLeft(TetrisRenderable *Tetris);
	
	bool MoveRight(TetrisRenderable *Tetris);
	// 
	bool MoveDown(TetrisRenderable *Tetris);
	// 当可控制的方块不能移动时,判断是否需要消行	
	int LineDelete(){
		int lineCount = 0;
		for (int i = mArrayHeight - 1; i >= 0;){
			bool IsFull = true;
			for (int j = 0; j < mArrayWidth; j++){
				int pos = mArrayWidth * i + j;
				if ((*mRenderArray)[pos] == 0)
				{
					IsFull = false;
					break;
				}
			}
			if (IsFull){
				lineCount++;
				MoveLine(i);
			}
			else{
				i--;
			}
		}
		return lineCount;
	}
	//消去一行后,上面的行往下移
	void MoveLine(int lineIndex){
		for (int i = lineIndex - 1; i >= 0; i--){
			for (int j = 0; j < mArrayWidth; j++){
				int pos = mArrayWidth * i + j;
				int nextLine = mArrayWidth * (i+1) + j;
				(*mRenderArray)[nextLine] = (*mRenderArray)[pos];
			}
		}
	}
	//判断是否满屏,在消完行之后进行判断
	bool IsGameOver(){
		for (int i = 0; i < mArrayWidth; i++){
			if ((*mRenderArray)[i] > 0){
				return true;
			}
		}
		return false;
	}	
};

方块场景

#pragma once
#include <SDL.h>
#include "Scene.h"
#include "TetrisRenderable.h"
#include "TextRenderable.h"
#include "TetrisWorldRenderable.h"


class TetrisScene : public Scene{
private :
	TextRenderable *mTextScore;
	TextRenderable *mTextLineCount;
	TextRenderable *mTextLevel;

	TetrisWorldRenderable *mTetrisWorld;
	//当前可控制的方块和下一个将出现的方块
	TetrisRenderable *mCurrentTetris, *mNextTetris;
	int mLevel, mLineCount, mScore;
	//下落速度,数字越小,速度越快
	float mDownSpeed;
	//经过的时间,如果时间大于downSpeed,可控制的方块往下移动一行
	float mTimeElapse;
public:
	TetrisScene(){
		mTextLevel = NULL;
		mTextLineCount = NULL;
		mTextLevel = NULL;
		mTetrisWorld = NULL;
		mCurrentTetris = NULL;
		mNextTetris = NULL;
		mLevel = 1;
		mDownSpeed = 0.5f;
		mTimeElapse = 0.0f;
		mLineCount = 0;
		mScore = 0;
	}
	~TetrisScene(){}

	virtual void LoadScene();
	virtual void HandleEvent(SDL_Event &_event);	
	virtual void Update(float time_step);
	void UpdateSocre(int line);
};

#include "stdafx.h"
#include "Common.h"
#include "TetrisScene.h"
#include "SDLGame.h"
#include "Colors.h"

//加载场景,3个文字显示,一个方块主界面,两个方块(当前,下一个)。
void TetrisScene::LoadScene(){
	mTetrisWorld = new TetrisWorldRenderable(15, 21);
	int wX = 0, wY = 0;
	if (mTetrisWorld->LoadTexture(theGame.GetResourcePath() + "tetris.png")){
		wX = (theGame.GetWindowWidth() - mTetrisWorld->GetWorldWidth()) / 2;
		wY = (theGame.GetWindowHeight() - mTetrisWorld->GetWorldHeight()) / 2;
		mTetrisWorld->SetPos(wX, wY);
		wX += mTetrisWorld->GetWorldWidth() + 20;
		wY += 20;
		this->AddRenderable(mTetrisWorld);
	}
	else {
		SAFE_DELETE(mTetrisWorld);
	}
	mCurrentTetris = new TetrisRenderable();
	if (mCurrentTetris->LoadTexture(theGame.GetResourcePath() + "tetris.png")){
		mCurrentTetris->SetArray(theTetrisArray.GetArray());
		mCurrentTetris->SetVisible(false);		
		this->AddRenderable(mCurrentTetris);
	}
	else{
		SAFE_DELETE(mCurrentTetris);
	}
	mNextTetris = new TetrisRenderable();
	if (mNextTetris->LoadTexture(theGame.GetResourcePath() + "tetris.png")){
		mNextTetris->SetPos(wX, wY);		
		mNextTetris->SetArray(theTetrisArray.GetArray());		
		this->AddRenderable(mNextTetris);
	}
	else{
		SAFE_DELETE(mNextTetris)
	}	
	int margin_top = 200;
	mTextLevel = new TextRenderable();
	char tempString[50];
	memset(tempString, 0, sizeof(tempString));
	SDL_Color color = Colors.WHITE;
	sprintf_s(tempString, sizeof(tempString), "Lv.%d", mLevel);
	if (mTextLevel->SetText(tempString,20,color)){
		mTextLevel->SetPos(wX + 10, wY + margin_top);
		this->AddRenderable(mTextLevel);
	}
	else{
		SAFE_DELETE(mTextLevel);
	}
	mTextLineCount = new TextRenderable();
	mTextScore = new TextRenderable();
	memset(tempString, 0, sizeof(tempString));
	sprintf_s(tempString, sizeof(tempString), "Score:%d", mScore);
	if (mTextScore->SetText(tempString,20,color)){
		mTextScore->SetPos(wX + 10, wY + margin_top + 30);
		this->AddRenderable(mTextScore);
	}
	else{
		SAFE_DELETE(mTextScore);
	}
	memset(tempString, 0, sizeof(tempString));
	sprintf_s(tempString, sizeof(tempString), "Line:%d", mLineCount);
	if (mTextLineCount->SetText(tempString, 20, color)){
		mTextLineCount->SetPos(wX + 10, wY + margin_top + 60);
		this->AddRenderable(mTextLineCount);
	}
	else{
		SAFE_DELETE(mTextLineCount);
	}
}
//方块控制
void TetrisScene::HandleEvent(SDL_Event &_event){
	if (_event.type == SDL_KEYDOWN){
		if (_event.key.keysym.sym == SDLK_k){
			mTetrisWorld->Rotate(mCurrentTetris);
		}
		else if (_event.key.keysym.sym == SDLK_a || _event.key.keysym.sym == SDLK_LEFT){
			mTetrisWorld->MoveLeft(mCurrentTetris);
		}
		else if (_event.key.keysym.sym == SDLK_d || _event.key.keysym.sym == SDLK_RIGHT){
			mTetrisWorld->MoveRight(mCurrentTetris);
		}
		else if (_event.key.keysym.sym == SDLK_s || _event.key.keysym.sym == SDLK_DOWN){	//如果当前的方块不能再往下移动了		
			if (!mTetrisWorld->MoveDown(mCurrentTetris)){
				int w, h;
				//下一个方块的值给当前方块
				mCurrentTetris->SetArray(mNextTetris->GetRenderTetris(w, h));
				//随机生成下一个方块
				mNextTetris->SetArray(theTetrisArray.GetArray());
				//重设当前方块的位置
				mTetrisWorld->SetTetrisStartPos();
				//消行,是否结束,更新得分
				int LineCount = mTetrisWorld->LineDelete();
				if (mTetrisWorld->IsGameOver()){
					mTetrisWorld->ResetArray();
					LineCount = -1;
				}
				UpdateSocre(LineCount);
			}
		}
	}
}
//
void TetrisScene::Update(float time_step){
	mTimeElapse += time_step;
	//自动下落
	if (mTimeElapse >= mDownSpeed){
		mTimeElapse -= mDownSpeed;
		if (!mTetrisWorld->MoveDown(mCurrentTetris)){
			
			int w, h;
			mCurrentTetris->SetArray(mNextTetris->GetRenderTetris(w, h));
			mNextTetris->SetArray(theTetrisArray.GetArray());
			mTetrisWorld->SetTetrisStartPos();
			int LineCount = mTetrisWorld->LineDelete();
			if (mTetrisWorld->IsGameOver()){
				mTetrisWorld->ResetArray();
				LineCount = -1;
			}
			UpdateSocre(LineCount);
		}
	}
}
void TetrisScene::UpdateSocre(int line){
	//一次性消除的行越多分数越高
	if (line > 0){
		int score = 100;
		switch (line)
		{		
		case 2:
			score = 300;
			break;
		case 3:
			score = 800;
			break;
		case 4:
			score = 1500;
			break;
		default:
			break;
		}
		mScore += score;
		mLineCount += line;
		char tempString[50];
		memset(tempString, 0, sizeof(tempString));
		sprintf_s(tempString, sizeof(tempString), "Score:%d", mScore);
		mTextScore->SetText(tempString, 20, Colors.WHITE);
		memset(tempString, 0, sizeof(tempString));
		sprintf_s(tempString, sizeof(tempString), "Line:%d", mLineCount);
		mTextLineCount->SetText(tempString, 20, Colors.WHITE);
	}
	else if (line == -1){
		//-1表示重置得分和等级
		char tempString[50];
		mScore = 0;
		mLineCount = 0;
		mLevel = 1;
		memset(tempString, 0, sizeof(tempString));
		sprintf_s(tempString, sizeof(tempString), "Score:%d", mScore);
		mTextScore->SetText(tempString, 20, Colors.WHITE);
		memset(tempString, 0, sizeof(tempString));
		sprintf_s(tempString, sizeof(tempString), "Line:%d", mLineCount);
		mTextLineCount->SetText(tempString, 20, Colors.WHITE);
		memset(tempString, 0, sizeof(tempString));
		sprintf_s(tempString, sizeof(tempString), "Lv.%d", mLevel);
		mTextLevel->SetText(tempString, 20, Colors.WHITE);
	}
}

测试

测试了移动,旋转。估计应该都没有什么问题。下一步将加入游戏音效。

int main(int argc, _TCHAR* argv[])
{	
	//获取程序所在路径
	char path[MAX_PATH];
	memset(path, 0, MAX_PATH);
	DWORD pathSize = GetModuleFileNameA(NULL, path, MAX_PATH);
	PathRemoveFileSpecA(path);
	
	//设置游戏资源所在路径
	string strPath = charToUTF8(path)+"\\Res\\";
	theGame.SetResourcePath(strPath);
	string title = charToUTF8("俄罗斯方块");	
	theGame.Init(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT);				

	//加载字体
	theGame.LoadGameFont(theGame.GetResourcePath() + "simkai.ttf");

	//创建场景
	Scene *loadingScene = new LoadingScene();
	loadingScene->LoadScene();
	theSceneManager.AddScene(LOADING, loadingScene);	
	//标题
	Scene* startScene = new StartScene();
	startScene->LoadScene();
	theSceneManager.AddScene(START, startScene);
	//俄罗斯方块
	Scene*mainScene = new TetrisScene();
	mainScene->LoadScene();
	theSceneManager.AddScene(MAIN, mainScene);
	
	//直接显示俄罗斯方块界面
	theSceneManager.ChangeScene(MAIN);

	theGame.Run();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

发布了9 篇原创文章 · 获赞 0 · 访问量 1030

猜你喜欢

转载自blog.csdn.net/drizzlemao/article/details/103449222