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);
	}
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章