SDL2 游戏开发日记(八) 按钮、对话框的绘制

SDL2 游戏开发日记(八) 按钮、对话框的绘制

在游戏中,会弹出各种各样的对话框,用来显示游戏中的一些信息,或者要求玩家进行相应的输入。

对话框的基类

创建一个纹理,把对话框的背景,按钮都绘制在这个纹理上。如果按钮状态没有发生改变,直接在主循环里绘制这个纹理。如果按钮状态改变,重新绘制纹理后再绘制到主循环里。

#pragma once
#include "Renderable.h"
#include "Button.h"
#include "Common.h"
#include "MessageListener.h"
#include <vector>
using namespace std;

class Dialog : public Renderable{
protected:
	//对话框背景
	Renderable *mBackground;
	//按钮列表
	vector<Button *>mButtonList;
	//是否需要重绘Texture;
	bool mIsChange;
public:
	Dialog(){
		mBackground = NULL;
	}
	//创建Texture;
	void CreateRenderTexture(int x, int y, int width, int height);
	virtual ~Dialog(){
		SAFE_DELETE(mBackground);
		
		SDL_DestroyTexture(mTexture);
	}
	//设置背景
	void SetBackground(Renderable*background){
		SAFE_DELETE(mBackground);
		mBackground = background;
	}
	//绘图
	virtual void Render();
	//加载按钮
	virtual void LoadButtons(){}
	//处理消息
	virtual void HandleEvent(SDL_Event &ev);
	//处理鼠标消息
	void HandleMouseEvent(int eventType, int x, int y);

	添加按钮
	void AddButton(Button *button){
		mButtonList.push_back(button);
	}

	void Refresh(){
		mIsChange = true;
	}
};

//Dialog.cpp
#include "stdafx.h"

#include "Dialog.h"
#include "SDLGame.h"

void Dialog::CreateRenderTexture(int x, int y, int width, int height){
	mTexture = SDL_CreateTexture(theGame.getRenderer(),
		SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
	if (mRenderRect == NULL)
		mRenderRect = new SDL_Rect();
	mTextureWidth = width;
	mTextureHeight = height;
	mRenderRect->w = width;
	mRenderRect->h = height;
	SetPos(x, y);
	mIsChange = true;
	SetLayer(10);
}

void Dialog::HandleEvent(SDL_Event &ev){
	if (ev.type == SDL_MOUSEMOTION){
		HandleMouseEvent(ev.type,ev.motion.x, ev.motion.y);
	}
	else if (ev.type == SDL_MOUSEBUTTONDOWN && ev.button.button == SDL_BUTTON_LEFT){
		HandleMouseEvent(ev.type,ev.button.x, ev.button.y);
	}
	else if (ev.type == SDL_MOUSEBUTTONUP && ev.button.button == SDL_BUTTON_LEFT){
		HandleMouseEvent(ev.type,ev.button.x, ev.button.y);
	}
}

void Dialog::Render(){
	if (!mIsVisible)
		return;
	if (mIsChange){
		SDL_SetRenderTarget(theGame.getRenderer(), mTexture);
		SDL_SetRenderDrawColor(theGame.getRenderer(), 0x00, 0x00, 0x00, 0x00);
		SDL_RenderClear(theGame.getRenderer());
		//			
		if (mBackground != NULL)
			mBackground->Render();
		vector<Button*>::iterator iter = mButtonList.begin();
		while (iter != mButtonList.end()){
			(*iter)->Render();
			iter++;
		}
		SDL_SetRenderTarget(theGame.getRenderer(), NULL);
		SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
		mIsChange = false;
	}
	SDL_RenderCopyEx(theGame.getRenderer(), mTexture, mClipRect, mRenderRect, 0, NULL, SDL_FLIP_NONE);
}

void Dialog::HandleMouseEvent(int eventType, int x, int y){
	x = x - mRenderRect->x;
	y = y - mRenderRect->y;
	vector<Button*>::iterator iter = mButtonList.begin();
	while (iter != mButtonList.end()){
		Button *btn = (*iter);
		if (btn->GetButtonState() == DISABLE)
		{
			iter++;
			continue;
		}
		if (eventType == SDL_MOUSEMOTION){
			if (btn->HandleMotion(x, y)){
				mIsChange = true;
			}
		}
		else if (eventType == SDL_MOUSEBUTTONDOWN){
			if (btn->HandleClick(x, y)){
				btn->SetButtonState(PRESS);
				mIsChange = true;
			}
		}
		else if (eventType == SDL_MOUSEBUTTONUP){
			if (btn->GetButtonState() != NORMAL){
				btn->SetButtonState(NORMAL);
				mIsChange = true;
			}
		}
		iter++;
	}
}

按钮类

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

#include <SDL.h>
#include <cassert>
#include <functional>	

//按钮状态
enum BUTTON_STATE{ NORMAL = 0, HOVER = 1,PRESS ,CHECK,UNCHECK, DISABLE };

//消息处理使用了回调函数的方式
//回调函数定义
typedef std::function <void(void *sender, void *args)>OnClickCallback;

class Button : public Renderable{
private:
	Renderable *mNormal;
	Renderable *mHover;
	Renderable *mPress;
	Renderable *mDisable;
	BUTTON_STATE mButtonState;
	OnClickCallback mClickCallback;
	void *mCallbackArgs;
public:	
	Button(){
		mNormal = NULL;
		mHover = NULL;
		mDisable = NULL;
		mPress = NULL;
	}
	Button(char*normal, char*hover,char *press, char *disable){
		assert(normal != NULL &&"normal state texture can't be NULL");

		if (normal != NULL){
			SetNormalRenderable(normal);
		}
		if (hover != NULL){
			SetHoverRenderable(hover);
		}
		if (disable != NULL){
			SetDisableRenderable(disable);
		}
		if (press != NULL){
			SetPressRenderable(press);
		}
	}
	virtual ~Button(){
		SAFE_DELETE(mNormal);
		SAFE_DELETE(mHover);
		SAFE_DELETE(mPress);
		SAFE_DELETE(mDisable);
	}
	
	//绘图
	virtual void Render();
	void SetNormalRenderable(string fileName);
	void SetHoverRenderable(string fileName);
	void SetDisableRenderable(string fileName);
	void SetPressRenderable(string fileName);
	//设置按钮状态
	void SetButtonState(BUTTON_STATE state){
		mButtonState = state;
	}
	//获取按钮状态
	BUTTON_STATE GetButtonState(){
		return mButtonState;
	}
	//处理鼠标移动消息
	bool HandleMotion(int x, int y);
	//处理鼠标左键单击消息
	bool HandleClick(int x, int y);
	//设置单击回调函数
	void SetClickCallbackFunc(OnClickCallback func,void *callbackArgs){
		mClickCallback = func;
		mCallbackArgs = callbackArgs;
	}
};	

//按钮的实现

#include "stdafx.h"
#include "Button.h"

void Button::SetHoverRenderable(string fileName){
	if (mHover == NULL){
		mHover = new Renderable();		
	}
	mHover->LoadTexture(fileName);
}
void Button::SetDisableRenderable(string fileName){
	if (mDisable == NULL){
		mDisable = new Renderable();
	}
	mDisable->LoadTexture(fileName);
}

void Button::SetNormalRenderable(string fileName){
	if (mNormal == NULL){
		mNormal = new Renderable();
		if (mRenderRect == NULL)
			mRenderRect = new SDL_Rect();		
	}
	mNormal->LoadTexture(fileName);
	mRenderRect->x = 0;
	mRenderRect->y = 0;
	mRenderRect->w = mNormal->GetWidth();
	mRenderRect->h = mNormal->GetHeight();
	mTextureWidth = mNormal->GetWidth();
	mTextureHeight = mNormal->GetHeight();
}
void Button::SetPressRenderable(string fileName){
	if (mPress == NULL){
		mPress = new Renderable();
	}
	mPress->LoadTexture(fileName);
}
//按钮的绘制,根据按钮的状态绘制对应的纹理
void Button::Render(){
	bool IsRender = false;
	if (mRenderRect == NULL)
		return;
	if (!mIsVisible){
		return;
	}
	switch (mButtonState){	
	case HOVER:
		if (mHover != NULL){
			IsRender = true;
			mHover->SetPos(mRenderRect->x, mRenderRect->y);
			mHover->Render();
		}
		break;
	case PRESS:
		if (mPress != NULL){
			IsRender = true;
			mPress->SetPos(mRenderRect->x, mRenderRect->y);
			mPress->Render();
		}
		break;
	case DISABLE:
		if (mDisable != NULL){
			IsRender = true;
			mDisable->SetPos(mRenderRect->x, mRenderRect->y);
			mDisable->Render();
		}
		break;
	}
	if (mNormal != NULL && !IsRender){
		mNormal->SetPos(mRenderRect->x, mRenderRect->y);
		mNormal->Render();
	}
}

bool Button::HandleMotion(int x, int y){
	if (mButtonState == DISABLE)
		return false;
	if (IsHit(x, y)){
		SetButtonState(HOVER);
		return true;
	}
	else{
		if (mButtonState == HOVER){
			SetButtonState(NORMAL);
			return true;
		}
	}
	return false;
}

bool Button::HandleClick(int x, int y){
	if (mButtonState == DISABLE)
		return false;
	if (IsHit(x, y)){
		SetButtonState(PRESS);
		//调用回调函数
		if (mClickCallback != NULL){
			mClickCallback(this, mCallbackArgs);
		}
		return true;
	}
	else{
		SetButtonState(NORMAL);
		return false;
	}
	return false;
}

使用示例:

这是一个麻将游戏的事件对话框。当产生碰、杠、胡的事件时,显示该对话框。

#pragma once
#include "../SDLGame/Button.h"
#include "../SDLGame/Renderable.h"
#include "../SDLGame/MessageListener.h"
#include "../SDLGame/Dialog.h"
#include <vector>
using namespace std;
class EventDialog : public Dialog{
private:	
	Button *mBtnPeng;
	Button *mBtnGang;
	Button *mBtnHu;
	Button *mBtnGiveup;			
	MessageListener *mReceiver;

public:
	EventDialog(){
		mBtnGang = NULL;
		mBtnHu = NULL;
		mBtnPeng = NULL;
		mBtnGiveup = NULL;			
	}
	virtual ~EventDialog(){

	}	
	virtual void LoadButtons(){
		mBackground = new Renderable();
		mBackground->LoadTexture("eventuibg.png");
		mBtnPeng = new Button("evtpeng0.png", "evtpeng1.png", NULL, "evtpeng2.png");
		mBtnGang = new Button("evtgang0.png", "evtgang1.png", NULL, "evtgang2.png");
		mBtnHu = new Button("evthu0.png", "evthu1.png", NULL, "evthu2.png");
		mBtnGiveup = new Button("giveup0.png", "giveup1.png", NULL, NULL);

		mBtnGiveup->SetPos((mTextureWidth - mBtnGiveup->GetWidth()) / 2, 5);
		mBtnPeng->SetPos(55, 105);
		mBtnGang->SetPos(145, 105);
		mBtnHu->SetPos(230, 105);
		//回调函数设置
		OnClickCallback callback = std::bind(&EventDialog::ButtonClick, this, std::placeholders::_1, std::placeholders::_2);
		mBtnGang->SetClickCallbackFunc(callback, NULL);
		mBtnPeng->SetClickCallbackFunc(callback, NULL);
		mBtnGiveup->SetClickCallbackFunc(callback, NULL);
		mBtnHu->SetClickCallbackFunc(callback, NULL);

		mIsChange = true;
		mButtonList.push_back(mBtnPeng);
		mButtonList.push_back(mBtnGang);
		mButtonList.push_back(mBtnHu);
		mButtonList.push_back(mBtnGiveup);
	}	

	//回调函数
	void ButtonClick(void*sender, void*args);
	//注册消息接收端
	void RegisterListener(MessageListener *listener){
		mReceiver = listener;
	}
	//
	void ShowEvent(int ev){
	
		if (ev & EVENT_HU){
			mBtnHu->SetButtonState(NORMAL);
		}
		else {
			mBtnHu->SetButtonState(DISABLE);
		}
		if (ev & EVENT_GANG){
			mBtnGang->SetButtonState(NORMAL);
		}
		else{
			mBtnGang->SetButtonState(DISABLE);
		}
		if (ev & EVENT_PENG){
			mBtnPeng->SetButtonState(NORMAL);
		}
		else {
			mBtnPeng->SetButtonState(DISABLE);
		}
		mIsChange = true;
		SetVisible(true);
	
	}
};

//回调函数
void EventDialog::ButtonClick(void *sender, void*args){
	if (mReceiver != NULL){
		int e = 0;
		if (sender == mBtnPeng)
			e = 1;
		else if (sender == mBtnGang)
			e = 2;
		else if (sender == mBtnHu)
			e = 3;
		//自定义消息
		Dispatcher.DispatchMsg(0.0f, this, mReceiver, e);
	}
}

SDL2 游戏开发日记(八) 按钮、对话框的绘制

上一篇:设置SDL编译环境


下一篇:VS2017配置SDL2