当下的智能机除了音量键、Home键外,几乎没有多余的按键,因而有一部分游戏提供了虚拟摇杆。

SDL2.x并没有提供虚拟摇杆相关的代码,不过实现起来并不算困难;虚拟摇杆包括绘图和事件处理:前者提供视觉效果,后者则捕获事件并作出响应。

示例结果如下:

图1-虚拟摇杆演示程序

本示例中大约有四个类:

  1. Game类 主要包含了初始化、事件处理、纹理绘制等功能,是本示例的核心类;
  2. Player类 玩家类,摇杆控制玩家进行移动,主要负责显示;
  3. ShotStick类 虚拟摇杆类,接收事件并处理事件;
  4. TextureManager类 纹理管理单例类,负责加载纹理,并把图片按照键值对保存。

在SDL游戏开发之六-简单的SDL程序一节中,有对Game类以及SDL的游戏流程做了一个简单的介绍,对于Game类的结构在这里不再赘述。

1. Game类

首先简单地说明一下Game的函数。

1.1 Game::init

bool Game::init(const char *title, int xpos, int ypos, int width, int height, int flags)
{m_bRunning = false;if (SDL_Init(SDL_INIT_EVERYTHING) == 0){/// if succeeded create our windowm_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags);if (m_pWindow != NULL)m_pRenderer = SDL_CreateRenderer(m_pWindow, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);if (m_pRenderer != NULL)SDL_SetRenderDrawColor(m_pRenderer,210,250,255,255);elsereturn false;}elsereturn false;

先对SDL库进行初始化,然后创建了窗口和渲染器。

 m_bRunning = true;std::string platform = SDL_GetPlatform();// initif (platform == "Android") {SDL_GetWindowSize(m_pWindow,&m_gameWidth,&m_gameHeight);}else {m_gameWidth = width;m_gameHeight = height;}SDL_Log("width=%d, height=%d\n", m_gameWidth, m_gameHeight);

在桌面操作系统下如windows,SDL会根据传递的窗口大小进行创建窗口;而对于搭载了android系统等的手机来说,其窗口大小就是手机的分辨率。(当然也是可以通过SDL_SetScale设置缩放比,不过会造成图片不同程度的拉伸)

 //结合SDL_RendererTheTextureManager::Instance()->bind(m_pRenderer);/*加载图片资源*/try{TheTextureManager::Instance()->load("Resources/icon.png","player");TheTextureManager::Instance()->load("Resources/shotStick1.png","shotStick1");TheTextureManager::Instance()->load("Resources/shotStick.png","shotStick2");}catch (std::runtime_error& e){std::cout << e.what() << std::endl;return false;}m_pPlayer = new Player();m_pPlayer->init("player");m_pShotStick = new ShotStick();m_pShotStick->init("shotStick1", "shotStick2");return true;

在Game::init函数的后半段,先是加载了所需要的图片资源,然后创建了一个玩家对象和虚拟摇杆对象。

1.2 Game::render

void Game::render()
{SDL_SetRenderDrawColor(m_pRenderer,210,250,255,255);///clear the renderer to the draw colorSDL_RenderClear(m_pRenderer);///drawSDL_SetRenderDrawColor(m_pRenderer, 0, 0, 0, 255);SDL_Rect rect = { 0, 0, 200, 200 };SDL_RenderDrawRect(m_pRenderer, &rect);m_pPlayer->draw(m_pRenderer);m_pShotStick->draw(m_pRenderer);///draw to the screenSDL_RenderPresent(m_pRenderer);
}

Game::render()负责渲染。后绘制的图片可能会遮挡之前绘制的图片,所以需要确认好绘制次序(其中的几句代码还绘制了一个黑色矩形,与本例无关,只是提醒在SDL中绘制图形,需要先把渲染器的绘制颜色和清屏颜色不同才行)

1.3 Game::handleEvents

void Game::handleEvents()
{SDL_Event event;while (SDL_PollEvent(&event)){switch (event.type){case SDL_QUIT:m_bRunning = false;break;}m_pShotStick->handleEvents(&event);}auto velocity = m_pShotStick->getVelocity();m_pPlayer->setVelocity(velocity);
}

Game::handlerEvents()负责处理。有事件发生时则会交给摇杆对象进行处理,之后再根据摇杆对象的偏移程度来更改操作玩家对象。

1.4 Game::update

void Game::update()
{m_pPlayer->update();
}

Game::handleEvents()只是负责设置玩家的速度,在update函数中还需要把速度乘以时间转为距离。

2. ShotStick 摇杆类

2.1 初始化函数

bool ShotStick::init(const std::string& rockerID, const std::string& backgroundID)
{m_rockerID = rockerID;m_backgroundID = backgroundID;//获取摇杆rectSDL_Rect rect1 = TheTextureManager::Instance()->getTextureRectFromId(rockerID);SDL_Rect rect2 = TheTextureManager::Instance()->getTextureRectFromId(backgroundID);//圆半径m_outCircle.radius = rect1.w/2;m_inCircle.radius = rect2.w/2;return true;
}

init函数会保存摇杆和摇杆背景图片的键名,以便于确定位置和进行绘制。

2.2 绘制函数

void ShotStick::draw(SDL_Renderer * ren)
{// 画出虚拟摇杆画出外圆int screenW = TheGame::getInstance()->getGameWidth();int screenH = TheGame::getInstance()->getGameHeight();// 圆心m_inCircle.x = m_outCircle.x = m_outCircle.radius;m_inCircle.y = m_outCircle.y = screenH - m_outCircle.radius;TheTextureManager::Instance()->draw(m_rockerID,m_outCircle.x - m_outCircle.radius,m_outCircle.y - m_outCircle.radius);// 画出内圆TheTextureManager::Instance()->draw(m_backgroundID,m_inCircle.x + m_relativePoint.x - m_inCircle.radius,m_inCircle.y + m_relativePoint.y - m_inCircle.radius);
}

虚拟摇杆默认显示在左下角。

2.3 ShotStick::handleEvents

void ShotStick::handleEvents(SDL_Event* event)
{switch (event->type){case SDL_MOUSEBUTTONDOWN:{if (event->button.button != SDL_BUTTON_LEFT)break;Sint32 x = event->motion.x, y = event->motion.y;auto distance = std::sqrt(std::pow(x - m_outCircle.x, 2) + std::pow(y - m_outCircle.y, 2));//超出距离if (distance > m_outCircle.radius) {m_fingerId = -1;break;}elsem_fingerId = 0;}

当鼠标左键按下时会触发SDL_MOUSEBUTTONDOWN并且event->button.button的值是SDL_BUTTON_LEFT。

为便于在电脑上调试,虚拟摇杆会接收鼠标左键事件,判断按下的点是否在图2所示的半透明背景内。只有在背景圆内才表示一次有效的按键。

图2 虚拟摇杆
     case SDL_MOUSEMOTION:{if (m_fingerId == -1)break;Sint32 x = event->motion.x, y = event->motion.y;//保证不会超过摇杆背景圆double x_average = x - m_outCircle.x;double y_average = y - m_outCircle.y;double d = std::sqrt(std::pow(x_average, 2) + std::pow(y_average, 2));double m = d > m_outCircle.radius ? m_outCircle.radius : d;m_relativePoint.x = m * (x_average / d);m_relativePoint.y = m * (y_average / d);}break;

当处理SDL_MOUSEBUTTONDOWN事件中产生了一个有效的点击后,再发生移动则会计算摇杆的圆心到摇杆背景圆心的距离,得到的值保存到m_relativePoint中。

     case SDL_MOUSEBUTTONUP:{if (event->button.button != SDL_BUTTON_LEFT)break;m_fingerId = -1;m_relativePoint.x = 0.0;m_relativePoint.y = 0.0;}break;

当产生鼠标左按键松开后,摇杆归零。

2.4 ShotStick::getVelocity

Vector2D ShotStick::getVelocity()
{double x = m_relativePoint.x;double y = m_relativePoint.y;double r = std::sqrt(x * x + y * y);if (r == 0)return Vector2D(0, 0);/* sin = y/r; cos = x/r; */r = m_outCircle.radius;return Vector2D(x / r, y / r);
}

图3 偏移的计算

如图3所示,O2减去O1则是m_relativePoint的值,再根据勾股定理则可以知道r的值。当r==0时,m_relativePoint=(0, 0);在r != 0后,又把r赋值为摇杆背景圆的半径大小,这样能保证其值域在[0, 1]之间。这样做的好处就是返回的速度不仅和方向有关,还和速度有关。当不为0时,则返回归一化后的值(值域[0, 1])。

后续还有则是在移动平台下的处理,效果同上类似,应该没什么问题(很久之前测试过)

     case SDL_FINGERDOWN:{SDL_Finger finger;finger.x = event->tfinger.x * TheGame::getInstance()->getGameWidth();finger.y = event->tfinger.y * TheGame::getInstance()->getGameHeight();// 绑定有效idif (m_fingerId == -1&& std::sqrt(std::pow(finger.x - m_outCircle.x, 2) +std::pow(finger.y - m_outCircle.y,2)) <= m_outCircle.radius)m_fingerId = event->tfinger.fingerId;}case SDL_FINGERMOTION:{SDL_FingerID id = event->tfinger.fingerId;// 如果不相等,退出if (m_fingerId != id)break;SDL_Finger finger;finger.id = id;finger.x = event->tfinger.x * TheGame::getInstance()->getGameWidth();finger.y = event->tfinger.y * TheGame::getInstance()->getGameHeight();double x_average = finger.x - m_outCircle.x;double y_average = finger.y - m_outCircle.y;double d = std::sqrt(std::pow(x_average, 2) + std::pow(y_average, 2));double m = d > m_outCircle.radius ? m_outCircle.radius : d;m_relativePoint.x = m * (x_average / d);m_relativePoint.y = m * (y_average / d);}break;case SDL_FINGERUP:{SDL_FingerID id = event->tfinger.fingerId;// 如果为有效idif (id == m_fingerId){m_relativePoint.x = 0;m_relativePoint.y = 0;m_fingerId = -1;}}break;

移动平台下和桌面操作系统类似,只不过移动平台下一般为多点触碰,所以也是需要绑定SDL_Finger的id的(类似于只有鼠标左键才能操作)。

3. Player类

玩家类则相对比较简单,只是负责显示精灵和确认位置而已。

bool Player::init(const string& spriteID)
{m_spriteID = spriteID;return true;
}void Player::setVelocity(const Vector2D& velocity)
{m_velocity = velocity;
}void Player::draw(SDL_Renderer*ren)
{TheTextureManager::Instance()->draw(m_spriteID,(int)m_position.getX(),(int)m_position.getY());
}void Player::update()
{m_velocity *= 2;m_position += m_velocity;//m_velocity += m_acceleration;
}void Player::clean()
{
}

代码:https://github.com/sky94520/ShotStick

SDL游戏开发之七-虚拟摇杆相关推荐

  1. cocos2dx游戏开发学习——虚拟摇杆(8方向)讲解

    写这篇博客的目的主要是记录一下 虚拟摇杆的实现过程.虚拟摇杆一般分文四方向和八方向,也主要根据项目需求来决定.直接进入主题吧. 先上效果图: 方向的思路分析 看图,说先我们可以将8个方向在坐标系中画出 ...

  2. SDL游戏开发系列第一话:Hello SDL

      各位读者朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://qinyuanpei.com.从今天起博主将带领大家一起走进SDL游戏开发的世界,如果说此前的Unity3D游戏开 ...

  3. SDL游戏开发之一-SDL的简介

    本教程为一个长系列,旨在于从零开始边学习SDL边开发游戏. 一.什么是SDL? SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成.SDL提 ...

  4. 《Cocos Creator游戏实战》虚拟摇杆实现

    虚拟摇杆实现 摇杆布局实现 摇杆功能实现 用摇杆控制主角 该功能已收录在Many Widgets插件中,使用Cocos Creator 3.x版本的小伙伴可以用该插件快速生成摇杆. 插件地址:http ...

  5. SDL游戏开发之三-瓦片地图

    一.瓦片地图 1)瓦片地图简介 瓦片地图(Tiled Map),又称为瓷砖地图,是在游戏开发中经常使用到的技术,它是由少量的尺寸相同的.小的瓦片图片拼接而成的很大的地图.相对于使用一张张图片来绘制地图 ...

  6. 【Android游戏开发之七】(游戏开发中需要的样式)再次剖析游戏开发中对SurfaceView中添加组件方案!...

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/308.html   ...

  7. SDL游戏开发之四-卡马克卷轴

    上一篇实现了瓦片地图的绘制,但是单纯地使用上面的代码还是有些问题的,下面就来讨论一下单纯使用瓦片地图的局限性. 假设游戏的分辨率为960*720,瓦片地图的大小也是960*720,瓦片大小为32,那么 ...

  8. 深入探索SDL游戏开发

    前言 欢迎来到小K的SDL专栏第二小节,本节将为大家带来基本窗口构成.渲染器.基本图形绘制.贴图.事件处理等的详细讲解,看完以后,希望对你有帮助 文章目录 前言 一.简单窗口 二.渲染器 三.基本图形 ...

  9. 2.SDL游戏开发:把代码写长一点(一)

    2019独角兽企业重金招聘Python工程师标准>>> 接着上一篇讲,里面有几个值得研究的函数,尽管理API里已经说得很清楚了,调用SDL_Init()动态的加载和初始化SDL库. ...

  10. 【iOS-Cocos2d游戏开发之七】添加/删除系统组件,并解决View设置透明会影响View中的其他组件的问题!...

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2d/455.html ...

最新文章

  1. python能做游戏吗-python制作小游戏(一)
  2. java字符串 大括号_string.format格式化字符串中转义大括号“{}”
  3. newLisp使用初步
  4. 学生党如何拿到阿里技术offer:《阿里面试(成功)》
  5. 深入Java虚拟机:JVM中的Stack和Heap
  6. 根据函数名称调用函数
  7. office右键菜单修复_自从有了这个工具,我的Office操作已经快到慢不下来了!
  8. Fatal Python error: pycurl: libcurl link-time version is older than compile-time version
  9. sourceTree对git的新建项目、储藏代码、切换分支、回滚代码、提交代码
  10. vscode中安装webpack_leaflet-webpack 入门开发系列一初探篇(附源码下载)
  11. Verilog设计中的锁存器
  12. 2021-05-17 吾日三省吾身
  13. 冯诺依曼体系结构与计算机基本组成
  14. 详解单例模式线程安全
  15. 9008刷机 小米max2_小米Max2解锁教程_小米Max2一键解锁BL的方法
  16. 学习笔记(02):Qt视频教程-基础实例进阶-网页爬虫-图片抓取
  17. CSS基础(二)--盒模型与浮动
  18. 微风:AI新手入门学习教程
  19. 在打工人的角度学《劳动法》
  20. [数学公式]等差数列求和

热门文章

  1. 2022最新最全升级版【精品工具】用Appuploader发布上传iOS APP上架流程简单快速
  2. 儿童学python下哪个软件好_少儿学编程适合哪个软件
  3. UDS - 诊断协议和协议栈介绍
  4. Word 2007~2010手动双面打印设置
  5. Docker镜像使用详解
  6. mysql授权远程访问 网段_MySQL授权和开启远程访问
  7. 软件工程 第4版张海藩 pdf_2019年第4期软件工程造价师培训课程圆满结束
  8. 我自己的 psftp-cmd
  9. 用python的OCR实现自动拍照搜题
  10. 制度罚则-- 代码走查规范