Browse Source

[Virtual Gamepad] Add menu panel to top-right corner

pull/3280/head
staphen 4 years ago committed by Anders Jenbo
parent
commit
ccd8f114f8
  1. 16
      Source/controls/game_controls.cpp
  2. 46
      Source/controls/touch/event_handlers.cpp
  3. 31
      Source/controls/touch/event_handlers.h
  4. 86
      Source/controls/touch/gamepad.cpp
  5. 53
      Source/controls/touch/gamepad.h
  6. 28
      Source/controls/touch/renderers.cpp
  7. 20
      Source/controls/touch/renderers.h

16
Source/controls/game_controls.cpp

@ -100,6 +100,22 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, Game
#if defined(VIRTUAL_GAMEPAD) && !defined(USE_SDL1)
if (event.type == SDL_FINGERDOWN) {
if (VirtualGamepadState.menuPanel.charButton.isHeld && VirtualGamepadState.menuPanel.charButton.didStateChange) {
*action = GameAction(GameActionType_TOGGLE_CHARACTER_INFO);
return true;
}
if (VirtualGamepadState.menuPanel.questsButton.isHeld && VirtualGamepadState.menuPanel.questsButton.didStateChange) {
*action = GameAction(GameActionType_TOGGLE_QUEST_LOG);
return true;
}
if (VirtualGamepadState.menuPanel.inventoryButton.isHeld && VirtualGamepadState.menuPanel.inventoryButton.didStateChange) {
*action = GameAction(GameActionType_TOGGLE_INVENTORY);
return true;
}
if (VirtualGamepadState.menuPanel.mapButton.isHeld && VirtualGamepadState.menuPanel.mapButton.didStateChange) {
*action = GameActionSendKey { DVL_VK_TAB, false };
return true;
}
if (VirtualGamepadState.primaryActionButton.isHeld && VirtualGamepadState.primaryActionButton.didStateChange) {
if (!inGameMenu && !QuestLogIsOpen && !sbookflag)
*action = GameAction(GameActionType_PRIMARY_ACTION);

46
Source/controls/touch/event_handlers.cpp

@ -127,6 +127,18 @@ bool VirtualGamepadEventHandler::Handle(const SDL_Event &event)
return false;
}
if (charMenuButtonEventHandler.Handle(event))
return true;
if (questsMenuButtonEventHandler.Handle(event))
return true;
if (inventoryMenuButtonEventHandler.Handle(event))
return true;
if (mapMenuButtonEventHandler.Handle(event))
return true;
if (directionPadEventHandler.Handle(event))
return true;
@ -210,15 +222,15 @@ bool VirtualDirectionPadEventHandler::HandleFingerMotion(const SDL_TouchFingerEv
return true;
}
bool VirtualPadButtonEventHandler::Handle(const SDL_Event &event)
bool VirtualButtonEventHandler::Handle(const SDL_Event &event)
{
if (!virtualPadButton->isUsable()) {
virtualPadButton->didStateChange = virtualPadButton->isHeld;
virtualPadButton->isHeld = false;
if (!virtualButton->isUsable()) {
virtualButton->didStateChange = virtualButton->isHeld;
virtualButton->isHeld = false;
return false;
}
virtualPadButton->didStateChange = false;
virtualButton->didStateChange = false;
switch (event.type) {
case SDL_FINGERDOWN:
@ -235,7 +247,7 @@ bool VirtualPadButtonEventHandler::Handle(const SDL_Event &event)
}
}
bool VirtualPadButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event)
bool VirtualButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event)
{
if (isActive)
return false;
@ -244,30 +256,30 @@ bool VirtualPadButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &
float y = event.y;
Point touchCoordinates = ScaleToScreenCoordinates(x, y);
if (!virtualPadButton->area.Contains(touchCoordinates))
if (!virtualButton->Contains(touchCoordinates))
return false;
virtualPadButton->isHeld = true;
virtualPadButton->didStateChange = true;
virtualButton->isHeld = true;
virtualButton->didStateChange = true;
activeFinger = event.fingerId;
isActive = true;
return true;
}
bool VirtualPadButtonEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event)
bool VirtualButtonEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event)
{
if (!isActive || event.fingerId != activeFinger)
return false;
if (virtualPadButton->isHeld)
virtualPadButton->didStateChange = true;
if (virtualButton->isHeld)
virtualButton->didStateChange = true;
virtualPadButton->isHeld = false;
virtualButton->isHeld = false;
isActive = false;
return true;
}
bool VirtualPadButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event)
bool VirtualButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event)
{
if (!isActive || event.fingerId != activeFinger)
return false;
@ -276,9 +288,9 @@ bool VirtualPadButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent
float y = event.y;
Point touchCoordinates = ScaleToScreenCoordinates(x, y);
bool wasHeld = virtualPadButton->isHeld;
virtualPadButton->isHeld = virtualPadButton->area.Contains(touchCoordinates);
virtualPadButton->didStateChange = virtualPadButton->isHeld != wasHeld;
bool wasHeld = virtualButton->isHeld;
virtualButton->isHeld = virtualButton->Contains(touchCoordinates);
virtualButton->didStateChange = virtualButton->isHeld != wasHeld;
return true;
}

31
Source/controls/touch/event_handlers.h

@ -29,10 +29,10 @@ private:
bool HandleFingerMotion(const SDL_TouchFingerEvent &event);
};
class VirtualPadButtonEventHandler {
class VirtualButtonEventHandler {
public:
VirtualPadButtonEventHandler(VirtualPadButton *virtualPadButton)
: virtualPadButton(virtualPadButton)
VirtualButtonEventHandler(VirtualButton *virtualButton)
: virtualButton(virtualButton)
, activeFinger(0)
, isActive(false)
{
@ -41,7 +41,7 @@ public:
bool Handle(const SDL_Event &event);
private:
VirtualPadButton *virtualPadButton;
VirtualButton *virtualButton;
SDL_FingerID activeFinger;
bool isActive;
@ -53,7 +53,11 @@ private:
class VirtualGamepadEventHandler {
public:
VirtualGamepadEventHandler(VirtualGamepad *virtualGamepad)
: directionPadEventHandler(&virtualGamepad->directionPad)
: charMenuButtonEventHandler(&virtualGamepad->menuPanel.charButton)
, questsMenuButtonEventHandler(&virtualGamepad->menuPanel.questsButton)
, inventoryMenuButtonEventHandler(&virtualGamepad->menuPanel.inventoryButton)
, mapMenuButtonEventHandler(&virtualGamepad->menuPanel.mapButton)
, directionPadEventHandler(&virtualGamepad->directionPad)
, primaryActionButtonEventHandler(&virtualGamepad->primaryActionButton)
, secondaryActionButtonEventHandler(&virtualGamepad->secondaryActionButton)
, spellActionButtonEventHandler(&virtualGamepad->spellActionButton)
@ -66,15 +70,20 @@ public:
bool Handle(const SDL_Event &event);
private:
VirtualButtonEventHandler charMenuButtonEventHandler;
VirtualButtonEventHandler questsMenuButtonEventHandler;
VirtualButtonEventHandler inventoryMenuButtonEventHandler;
VirtualButtonEventHandler mapMenuButtonEventHandler;
VirtualDirectionPadEventHandler directionPadEventHandler;
VirtualPadButtonEventHandler primaryActionButtonEventHandler;
VirtualPadButtonEventHandler secondaryActionButtonEventHandler;
VirtualPadButtonEventHandler spellActionButtonEventHandler;
VirtualPadButtonEventHandler cancelButtonEventHandler;
VirtualButtonEventHandler primaryActionButtonEventHandler;
VirtualButtonEventHandler secondaryActionButtonEventHandler;
VirtualButtonEventHandler spellActionButtonEventHandler;
VirtualButtonEventHandler cancelButtonEventHandler;
VirtualPadButtonEventHandler healthButtonEventHandler;
VirtualPadButtonEventHandler manaButtonEventHandler;
VirtualButtonEventHandler healthButtonEventHandler;
VirtualButtonEventHandler manaButtonEventHandler;
};
void HandleTouchEvent(const SDL_Event &event);

86
Source/controls/touch/gamepad.cpp

@ -51,6 +51,7 @@ void InitializeVirtualGamepad()
{
int screenPixels = std::min(gnScreenWidth, gnScreenHeight);
int inputMargin = screenPixels / 10;
int menuButtonWidth = screenPixels / 10;
int directionPadSize = screenPixels / 4;
int padButtonSize = round(1.1 * screenPixels / 10);
int padButtonSpacing = inputMargin / 3;
@ -67,11 +68,20 @@ void InitializeVirtualGamepad()
float dpi = std::min(hdpi, vdpi);
inputMargin = round(0.25 * dpi);
menuButtonWidth = round(0.2 * dpi);
directionPadSize = round(dpi);
padButtonSize = round(0.3 * dpi);
padButtonSpacing = round(0.1 * dpi);
}
int menuPanelTopMargin = 30;
int menuPanelButtonSpacing = 4;
Size menuPanelButtonSize = { 64, 62 };
int rightMarginMenuButton4 = menuPanelButtonSpacing + menuPanelButtonSize.width;
int rightMarginMenuButton3 = rightMarginMenuButton4 + menuPanelButtonSpacing + menuPanelButtonSize.width;
int rightMarginMenuButton2 = rightMarginMenuButton3 + menuPanelButtonSpacing + menuPanelButtonSize.width;
int rightMarginMenuButton1 = rightMarginMenuButton2 + menuPanelButtonSpacing + menuPanelButtonSize.width;
int padButtonAreaWidth = round(std::sqrt(2) * (padButtonSize + padButtonSpacing));
int padButtonRight = gnScreenWidth - inputMargin - padButtonSize / 2;
@ -79,42 +89,74 @@ void InitializeVirtualGamepad()
int padButtonBottom = gnScreenHeight - inputMargin - padButtonSize / 2;
int padButtonTop = padButtonBottom - padButtonAreaWidth;
Rectangle &charButtonArea = VirtualGamepadState.menuPanel.charButton.area;
charButtonArea.position.x = gnScreenWidth - rightMarginMenuButton1 * menuButtonWidth / menuPanelButtonSize.width;
charButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width;
charButtonArea.size.width = menuButtonWidth;
charButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width;
Rectangle &questsButtonArea = VirtualGamepadState.menuPanel.questsButton.area;
questsButtonArea.position.x = gnScreenWidth - rightMarginMenuButton2 * menuButtonWidth / menuPanelButtonSize.width;
questsButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width;
questsButtonArea.size.width = menuButtonWidth;
questsButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width;
Rectangle &inventoryButtonArea = VirtualGamepadState.menuPanel.inventoryButton.area;
inventoryButtonArea.position.x = gnScreenWidth - rightMarginMenuButton3 * menuButtonWidth / menuPanelButtonSize.width;
inventoryButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width;
inventoryButtonArea.size.width = menuButtonWidth;
inventoryButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width;
Rectangle &mapButtonArea = VirtualGamepadState.menuPanel.mapButton.area;
mapButtonArea.position.x = gnScreenWidth - rightMarginMenuButton4 * menuButtonWidth / menuPanelButtonSize.width;
mapButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width;
mapButtonArea.size.width = menuButtonWidth;
mapButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width;
Rectangle &menuPanelArea = VirtualGamepadState.menuPanel.area;
menuPanelArea.position.x = gnScreenWidth - 399 * menuButtonWidth / menuPanelButtonSize.width;
menuPanelArea.position.y = 0;
menuPanelArea.size.width = 399 * menuButtonWidth / menuPanelButtonSize.width;
menuPanelArea.size.height = 162 * menuButtonWidth / menuPanelButtonSize.width;
VirtualDirectionPad &directionPad = VirtualGamepadState.directionPad;
directionPad.area.position.x = inputMargin + directionPadSize / 2;
directionPad.area.position.y = gnScreenHeight - inputMargin - directionPadSize / 2;
directionPad.area.radius = directionPadSize / 2;
directionPad.position = directionPad.area.position;
VirtualPadButton &primaryActionButton = VirtualGamepadState.primaryActionButton;
primaryActionButton.area.position.x = padButtonRight;
primaryActionButton.area.position.y = (padButtonTop + padButtonBottom) / 2;
primaryActionButton.area.radius = padButtonSize / 2;
Circle &primaryActionButtonArea = VirtualGamepadState.primaryActionButton.area;
primaryActionButtonArea.position.x = padButtonRight;
primaryActionButtonArea.position.y = (padButtonTop + padButtonBottom) / 2;
primaryActionButtonArea.radius = padButtonSize / 2;
VirtualPadButton &secondaryActionButton = VirtualGamepadState.secondaryActionButton;
secondaryActionButton.area.position.x = (padButtonLeft + padButtonRight) / 2;
secondaryActionButton.area.position.y = padButtonTop;
secondaryActionButton.area.radius = padButtonSize / 2;
Circle &secondaryActionButtonArea = VirtualGamepadState.secondaryActionButton.area;
secondaryActionButtonArea.position.x = (padButtonLeft + padButtonRight) / 2;
secondaryActionButtonArea.position.y = padButtonTop;
secondaryActionButtonArea.radius = padButtonSize / 2;
VirtualPadButton &spellActionButton = VirtualGamepadState.spellActionButton;
spellActionButton.area.position.x = padButtonLeft;
spellActionButton.area.position.y = (padButtonTop + padButtonBottom) / 2;
spellActionButton.area.radius = padButtonSize / 2;
Circle &spellActionButtonArea = VirtualGamepadState.spellActionButton.area;
spellActionButtonArea.position.x = padButtonLeft;
spellActionButtonArea.position.y = (padButtonTop + padButtonBottom) / 2;
spellActionButtonArea.radius = padButtonSize / 2;
VirtualPadButton &cancelButton = VirtualGamepadState.cancelButton;
cancelButton.area.position.x = (padButtonLeft + padButtonRight) / 2;
cancelButton.area.position.y = padButtonBottom;
cancelButton.area.radius = padButtonSize / 2;
Circle &cancelButtonArea = VirtualGamepadState.cancelButton.area;
cancelButtonArea.position.x = (padButtonLeft + padButtonRight) / 2;
cancelButtonArea.position.y = padButtonBottom;
cancelButtonArea.radius = padButtonSize / 2;
VirtualPadButton &healthButton = VirtualGamepadState.healthButton;
healthButton.area.position.x = directionPad.area.position.x - (padButtonSize + padButtonSpacing) / 2;
healthButton.area.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2;
healthButton.area.radius = padButtonSize / 2;
Circle &healthButtonArea = healthButton.area;
healthButtonArea.position.x = directionPad.area.position.x - (padButtonSize + padButtonSpacing) / 2;
healthButtonArea.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2;
healthButtonArea.radius = padButtonSize / 2;
healthButton.isUsable = []() { return !chrflag && !QuestLogIsOpen; };
VirtualPadButton &manaButton = VirtualGamepadState.manaButton;
manaButton.area.position.x = directionPad.area.position.x + (padButtonSize + padButtonSpacing) / 2;
manaButton.area.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2;
manaButton.area.radius = padButtonSize / 2;
Circle &manaButtonArea = manaButton.area;
manaButtonArea.position.x = directionPad.area.position.x + (padButtonSize + padButtonSpacing) / 2;
manaButtonArea.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2;
manaButtonArea.radius = padButtonSize / 2;
manaButton.isUsable = []() { return !chrflag && !QuestLogIsOpen; };
}

53
Source/controls/touch/gamepad.h

@ -7,6 +7,7 @@
#include "controls/controller_buttons.h"
#include "engine/circle.hpp"
#include "engine/point.hpp"
#include "engine/rectangle.hpp"
namespace devilution {
@ -31,22 +32,64 @@ struct VirtualDirectionPad {
void UpdatePosition(Point touchCoordinates);
};
struct VirtualPadButton {
Circle area;
struct VirtualButton {
bool isHeld;
bool didStateChange;
std::function<bool()> isUsable;
VirtualPadButton()
: area({ { 0, 0 }, 0 })
, isHeld(false)
VirtualButton()
: isHeld(false)
, didStateChange(false)
, isUsable([]() { return true; })
{
}
virtual bool Contains(Point point) = 0;
};
struct VirtualMenuButton : VirtualButton {
Rectangle area;
VirtualMenuButton()
: area({ { 0, 0 }, { 0, 0 } })
{
}
bool Contains(Point point) override
{
return area.Contains(point);
}
};
struct VirtualPadButton : VirtualButton {
Circle area;
VirtualPadButton()
: area({ { 0, 0 }, 0 })
{
}
bool Contains(Point point) override
{
return area.Contains(point);
}
};
struct VirtualMenuPanel {
VirtualMenuButton charButton;
VirtualMenuButton questsButton;
VirtualMenuButton inventoryButton;
VirtualMenuButton mapButton;
Rectangle area;
VirtualMenuPanel()
: area({ { 0, 0 }, { 0, 0 } })
{
}
};
struct VirtualGamepad {
VirtualMenuPanel menuPanel;
VirtualDirectionPad directionPad;
VirtualPadButton primaryActionButton;

28
Source/controls/touch/renderers.cpp

@ -190,11 +190,22 @@ void RenderVirtualGamepad(SDL_Surface *surface)
void VirtualGamepadRenderer::LoadArt(SDL_Renderer *renderer)
{
menuPanelRenderer.LoadArt(renderer);
directionPadRenderer.LoadArt(renderer);
LoadButtonArt(&buttonArt, renderer);
LoadPotionArt(&potionArt, renderer);
}
void VirtualMenuPanelRenderer::LoadArt(SDL_Renderer *renderer)
{
menuArt.surface.reset(LoadPNG("ui_art\\menu.png"));
if (renderer != nullptr) {
menuArt.texture.reset(SDL_CreateTextureFromSurface(renderer, menuArt.surface.get()));
menuArt.surface = nullptr;
}
}
void VirtualDirectionPadRenderer::LoadArt(SDL_Renderer *renderer)
{
padArt.surface.reset(LoadPNG("ui_art\\directions.png"));
@ -225,6 +236,17 @@ void VirtualGamepadRenderer::Render(RenderFunction renderFunction)
manaButtonRenderer.RenderPotion(renderFunction, potionArt);
directionPadRenderer.Render(renderFunction);
menuPanelRenderer.Render(renderFunction);
}
void VirtualMenuPanelRenderer::Render(RenderFunction renderFunction)
{
int x = virtualMenuPanel->area.position.x;
int y = virtualMenuPanel->area.position.y;
int width = virtualMenuPanel->area.size.width;
int height = virtualMenuPanel->area.size.height;
SDL_Rect rect { x, y, width, height };
renderFunction(menuArt, nullptr, &rect);
}
void VirtualDirectionPadRenderer::Render(RenderFunction renderFunction)
@ -450,11 +472,17 @@ VirtualGamepadButtonType PotionButtonRenderer::GetButtonType()
void VirtualGamepadRenderer::UnloadArt()
{
menuPanelRenderer.UnloadArt();
directionPadRenderer.UnloadArt();
buttonArt.Unload();
potionArt.Unload();
}
void VirtualMenuPanelRenderer::UnloadArt()
{
menuArt.Unload();
}
void VirtualDirectionPadRenderer::UnloadArt()
{
padArt.Unload();

20
Source/controls/touch/renderers.h

@ -51,6 +51,22 @@ enum VirtualGamepadPotionType {
typedef std::function<void(Art &art, SDL_Rect *src, SDL_Rect *dst)> RenderFunction;
class VirtualMenuPanelRenderer {
public:
VirtualMenuPanelRenderer(VirtualMenuPanel *virtualMenuPanel)
: virtualMenuPanel(virtualMenuPanel)
{
}
void LoadArt(SDL_Renderer *renderer);
void Render(RenderFunction renderFunction);
void UnloadArt();
private:
VirtualMenuPanel *virtualMenuPanel;
Art menuArt;
};
class VirtualDirectionPadRenderer {
public:
VirtualDirectionPadRenderer(VirtualDirectionPad *virtualDirectionPad)
@ -153,7 +169,8 @@ private:
class VirtualGamepadRenderer {
public:
VirtualGamepadRenderer(VirtualGamepad *virtualGamepad)
: directionPadRenderer(&virtualGamepad->directionPad)
: menuPanelRenderer(&virtualGamepad->menuPanel)
, directionPadRenderer(&virtualGamepad->directionPad)
, primaryActionButtonRenderer(&virtualGamepad->primaryActionButton)
, secondaryActionButtonRenderer(&virtualGamepad->secondaryActionButton)
, spellActionButtonRenderer(&virtualGamepad->spellActionButton)
@ -168,6 +185,7 @@ public:
void UnloadArt();
private:
VirtualMenuPanelRenderer menuPanelRenderer;
VirtualDirectionPadRenderer directionPadRenderer;
PrimaryActionButtonRenderer primaryActionButtonRenderer;

Loading…
Cancel
Save