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