游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

分步示意触屏设备虚拟控制器设计过程(一)

发布时间:2012-05-24 15:17:58 Tags:,,,

作者:Mike Jones

我觉得自己可以使用Pushbutton Engine作为基础来测试某些曾经玩过的虚拟控制器,所以编写了这个有关Pushbutton Engine的系列文章。使用这种方法使我无需将大量时间投入到代码编写中,能够更专注于真正的控制器及其整合方法。我真切地感受到,要让这种控制器整合尽量标准化,这样才能够被运用到任何游戏中,无需考虑代码的影响。(请点击此处阅读第二部分

我还做了些许假设,首先我用自己的拇指来决定控制器拇指杆的大小。我的拇指很大,所以我认为这样的大小应该能够满足多数人的需求。第二个假设显得更为技术化。对于船只或角色的控制而言,多数PC游戏对键盘输入有很强的依赖(游戏邦注:Flash网页游戏尤为突出),所以我试图模拟上、下、左和右4个方向键,而不是使用坐标转换。使用按键输入模拟取代坐标转换的原因很多。上述是第一个原因。第二是为了转换的便利性,因为多数使用键盘的Flash游戏已经添加了虚拟控制器,这样就无需对现有代码进行大幅更新。最后,采用这种方法有利于测试和调试。

virtual joystick(from toucharcade.com)

virtual joystick(from toucharcade.com)

设计控制器

首先要考虑的是控制器的视觉外观,本文将使用的是类似于Playstation或XBox控制器上的拇指杆。为了让用户更容易地使用拇指杆进行触碰输入,你需要两个元素。第一个是拇指区域,也就是用户放置拇指的地方,第二个元素是环绕。使用环绕有两个原因,既可以在视觉上让用户明白拇指杆移动的范围,也可以用次来作为整个控制器的边界,这样你就能够知道用户拇指的位置,更重要的是能够知道他们的拇指何时离开控制杆。下图便是整个拇指杆控制器(游戏邦注:包括拇指区域和环绕)的样式。

virtual joystick(from flashgen.com)

virtual joystick(from flashgen.com)

有趣的是,如果你不想要视觉呈现,你完全可以将其移除。有些游戏隐藏了拇指杆的呈现,只向用户标注拇指可放置位置。这种方法可能会让部分玩家感到困惑,所以是否完全移除视觉元素的决定应当取决于游戏的目标用户类型。如果不确定,可将控制器设置为透明风格。

因为这属于控制器的视觉层面,所以可以使用Flash Professional CS5来制作。你也可以使用Flash Builder来完成所有的工作,但是使用更具视觉化的工具会让初始化更加简单。创建新FLA文件,命名为VirtualControllers,将其保存在电脑上。接下来,新建ActionScript 3.0类,命名为ThumbStick.as,将其保存在与VirtualControllers.fla相同的目录下。我使用程序包结构来呈现类,所以文件夹就如下图所示。如果你不喜欢这种方法,也可以不这么做,但我们在本文将使用略显单调的程序包结构。

folder hierarchy(from flashgen.com)

folder hierarchy(from flashgen.com)

在我们开始编写代码钱,你需要在库中为控制器创建元件,导入环绕和拇指杆图片资产(游戏邦注:可以下载作者的设计,也可以自行设计)。

1、创建新元件(插入>新元件)

2、将新元件命名为ThumbStick

3、定位下载的环绕和拇指杆资产,将它们导入到库中(文件>导入>导入到库)

4、Flash Pro CS5会自动在元件库中创建两个新元件(Symbol1和Symbol2)

5、打开元件库(窗口>库)

6、选择Symbol1,根据其图像来重命名(可能是环绕或拇指杆)

7、对Symbol2重复这个步骤

将元件重命名后,你需要对它们进行编辑,确保所包含的图片位于元件的中心位置,同时确保它们被设置为影片剪辑元件而不是图像元件(游戏邦注:系统默认其为图像元件)。

1、右键点击库中的Surround元件

2、从背景菜单中选择属性

3、当属性对话框打开时,将上面的“类型”从图像切换为影片剪辑

4、点击“OK”应用并关闭这个对话窗口

5、对Thumb元件重复这个过程

6、在选中Thumb元件时双击进入编辑模式

7、面板应当更新为图片资产位于近屏幕中间位置

8、选择图片(周围出现蓝色方框)

9、在属性面板(窗口>属性)中将X和Y的值设为-45,这会将Thumb图片放置在中心位置,因为其大小是90 X 90像素

10、对Surround元件重复这个过程,将X和Y的值设为-75,因为该图片大小为150 X 150像素。

现在,你已经确定了拇指杆控制器的主要元素,你需要将它们组合起来。要实现这个目标,首先编辑你创建的名为ThumbStick的元件:

symbola lignment(from flashgen.com)

symbola lignment(from flashgen.com)

1、双击库中的ThumbStick元件进入编辑模式

2、将时间轴(窗口>时间轴)中的默认层重命名为Thumb。右键点击层,选择背景菜单中的“属性”

3、从打开的对话框中设置新的层名称。你也可以双击层直接进行编辑

4、在时间轴面板中创建新层(点击时间轴左下方的“新建层”图标),命名为Surround

5、确保选中Surround层

6、从库中拖出Surround元件,确保其位于元件的中心

7、选中Surround元件,打开属性面板,为环绕设置成员名称

8、当你对它的位置感到满意后,选择Thumb层

9、选中Thumb层,将Thumb元件拖动到ThumbStick元件上,确保其位于Thumb层上

10、同样确保Thumb元件位于中心位置(注意图片中位于元件中央的交叉瞄准线)

11、与Surround元件做法相同,打开属性面板(确保其处在选中状态下),为Thumb元件设置拇指成员名称

技巧

如果位图资产看起来有明显的锯齿,你可以用平滑来处理,右键点击库中的每个图片,从背景菜单中选择“属性”。打开对话框后,勾选“平滑”选框,点击“OK”应用。

现在,你已经拥有了FLA和ActionScript文件,我们可以开始创建初始化代码。你需要做的首件事情是确保类扩展精灵(游戏邦注:默认情况下它不会扩展)。你还需要用某些私有变量来存储控制器信息。我们需要使用下列代码(游戏邦注:只需要复制和粘贴下列代码即可):

定义控制器

现在,你已经拥有了设计,你已经创建了控制器元件,你需要开始考虑添加能够控制该元件的代码,这样用户与之发生互动时才会起作用。最佳方法是将这个过程分解成多个部分的功能列表。列表很可能如下所示:

1、以圆形动作移动拇指杆

2、将拇指杆移动限制在环绕内

3、当用户移开拇指时让拇指杆回到控制器的中心位置

4、当用户移动拇指杆时发送事件

显然,你可以随意在这个列表中添加更多的内容,以上这4个只是基本功能。接下来,我们开始编写代码。

编写代码

你要做的首件事情是确保你的ThumbStick类扩展精灵,默认情况下Flash Professional创建的是非继承类。现在,你的代码应当如下所示:

package
{
import flash.display.Sprite;

public class ThumbStick extends Sprite
{
public function ThumbStick()
{

}
}
}

回头看看上文我罗列的4个功能点,首个功能是以圆形动作移动拇指杆,所以让我们从移动拇指杆开始。这是个相当复杂的过程,我将留到本系列文章的第2部分作具体的阐述。但是,你要做的是设置控制器,这样当你触碰到拇指杆时,它会跟随你的拇指而移动,释放拇指后又会自行回到环绕的中心位置。

要实现这个目标,你需要添加几个事件监听器。这些监听器会处理用户用拇指触碰和移开拇指的触碰事件。尽管你可以直接与控制器中的元件互动(游戏邦注:因为在上文中已设置成员名称),但是Flash Professional代码编辑器中的代码较为复杂,所以我将使用元件中的私有变量使过程变得更加简单。

以下代码包含事件监听器及其处理器,你可以看到整个流程和结构(游戏邦注:这段代码中有个问题,作者稍后将进行解释):

package
{
import flash.display.Sprite;
import flash.events.TouchEvent;

public class ThumbStick extends Sprite
{
private var _thumb    :Sprite;
private var _surround    :Sprite;

public function ThumbStick()
{
_thumb = thumb;
_surround = surround;

_thumb.addEventListener(TouchEvent.TOUCH_BEGIN, onThumbDown);
_thumb.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
_surround.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
}

protected function onThumbDown(e:TouchEvent):void
{
trace(“Thumb Down”);
}

protected function onThumbUp(e:TouchEvent):void
{
trace(“Thumb Up”);
}
}
}

那么,这段代码中的问题是什么呢?与测试有关。你会发现,如果你从Flash Professional内部运行这段代码,你无法对TouchEvents进行测试,因为通常的PC与移动设备并不相同。如果你手头没有可供测试的实体设备,那么这段代码并不是特别有用。随后,我会强调某些情况下在目标硬件设备上测试游戏和应用程序的重要性。

改进方法是选择将来运行程序的硬件类型,以此为基础使用Capabilities()类调整代码。所以,如果我对上述代码进行更新,我就能够测试其是否能够在基于ARM的处理器(游戏邦注:多数智能手机和平板电脑使用的处理器)上运行。随后,我也能调整代码初始化的方法。

package
{
import flash.display.Sprite;
import flash.events.TouchEvent;
import flash.system.Capabilities;
import flash.events.MouseEvent;

public class ThumbStick extends Sprite
{
private var _thumb    :Sprite;
private var _surround    :Sprite;

public function ThumbStick()
{
_thumb = thumb;
_surround = surround;

if(Capabilities.cpuArchitecture == “ARM”)
{
_thumb.addEventListener(TouchEvent.TOUCH_BEGIN, onThumbDown);
_thumb.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
_surround.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
}
else
{
_thumb.addEventListener(MouseEvent.MOUSE_DOWN, onTestThumbDown);
_thumb.addEventListener(MouseEvent.MOUSE_UP, onTestThumbUp);
_surround.addEventListener(MouseEvent.MOUSE_UP, onTestThumbUp);
}
}

protected function onThumbDown(e:TouchEvent):void
{
trace(“Thumb Down”);
}

protected function onThumbUp(e:TouchEvent):void
{
trace(“Thumb Up”);
}

// These are just for testing within Flash Pro
protected function onTestThumbDown(e:MouseEvent):void
{
trace(“Mouse Down”);
}

protected function onTestThumbUp(e:MouseEvent):void
{
trace(“Mouse Up”);
}
}
}

这段代码较为有用,但是我倾向于在使用真正的设备测试之前移除所有的非必要代码。如果你不需要执行,那为何还要添加这些代码呢?让我们继续下去。

你需要执行的下个功能是拇指杆的移动。这很容易实现,我创造了两种方法:一种负责处理当用户触碰拇指杆时的移动,另一种负责当用户移开拇指时让拇指杆回到环绕中心点:

protected function initDrag():void
{
_thumb.startDrag();
}

protected function resetThumb():void
{
_thumb.stopDrag();
_thumb.x = 0;
_thumb.y = 0;
}

要访问这些方法,你需要在事件处理器的initDrag()中添加调用,在用户将拇指放在拇指杆上时作出响应,也就是onThumbDown()和onTestThumbDown()。同样,当用户从控制器上移开拇指时,系统需要调用resetThumb()方法,所以你需要在onThumbUp()和onTestThumbUp()方法中添加对这项命令的调用。

如果你现在进行测试,它的功能应当同以下范例类似。试着点击和拖动以下拇指杆。释放鼠标左键后,它会回到环绕中心的原位置上。应当注意的是,尽管你可以用鼠标点击和拖动拇指杆,但其并不受环绕边界的限制。这不是我们想要的结果。不过不用担心,接下来我们就要处理这个问题。

virtual joystick (from flashgen.com)

virtual joystick (from flashgen.com)

设置控制器边界

正如我之前提到的,现在我们已经实现了虚拟控制器的基本拖动功能,而且在释放鼠标后拇指杆会重新回到原位置。但是,它的移动并没有被限制在控制器环绕内。接下来,我们要处理的就是这个问题。

首先,你需要做的是添加新事件监听器,在移动拇指杆时对其进行监管,这样当它移动到控制器边界时就会受到限制。现在,通常情况下你可能会使用MouseEvent.MOUSE_MOVE事件来监管拇指杆的移动。但是,与Event.ENTER_FRAME事件相比,前者在事件分配和使用方面显得相当昂贵。记住这一点,将下列代码添加到initDrag()方法底部:

addEventListener(Event.ENTER_FRAME, onThumbDrag);

同样,当用户停止同控制器互动时,你需要移除这个事件。要实现这个目标,你只需要移除resetThumb()方法中的事件监听器。与initDrag()方法类似,将下列代码添加到resetThumb()方法前。这样,事件就会被直接移除,其他内务程序就会得到处理:

removeEventListener(Event.ENTER_FRAME, onThumbDrag);

你会注意到,这两行代码都涉及到目前还未执行的方法,也就是onThumbDrag()。这个方法用于处理所有同拇指杆移动边界设置相关的计算。以下是完整的onThumbDrag()方法代码,你需要在代码中执行。

protected function onThumbDrag(e:Event):void
{
// Store the current x/y of the knob
var _currentX        :Number = _thumb.x;
var _currentY        :Number = _thumb.y;

// Store the registration point of the surrounding ‘joystick holder’
var _registrationX    :Number = _surround.x;
var _registrationY    :Number = _surround.y;

// Subtract the two from each other to get the actual x/y
var _actualX    :Number = _currentX – _registrationX;
var _actualY    :Number =  _currentY – _registrationY;

// Calculate the degrees for use when creating the zones.
_degrees = Math.round(Math.atan2(_actualY, _actualX) * 180/Math.PI);

// Calculate the radian value of the knobs current position
var _angle         :Number = _degrees * (Math.PI / 180);

// As we want to lock the orbit of the knob we need to calculate x/y at the maximum distance
var _maxX        :Number = Math.round((_radius * Math.cos(_angle)) + _registrationX);
var _maxY        :Number = Math.round((_radius * Math.sin(_angle)) + _registrationY);

// Check to make sure that the value is positive or negative
if(_currentX > 0 && _currentX > _maxX || _currentX < 0 && _currentX < _maxX)
_thumb.x = _maxX;

if(_currentY > 0 && _currentY > _maxY || _currentY < 0 && _currentY < _maxY)
_thumb.y = _maxY;
}

在我分析这个方法的各个层面前,你或许会注意到这段代码中提及两个变量域。它们是_degrees和_radius。这两者用以下代码来定义:

private var _degrees    :Number;
private var _radius    :Number = 25;

_degrees以后再解释,_radius变量处理的是控制器边界内的移动范围。将该变量设置为25的原因在于,拇指杆的大小为50X50像素,一半就是25,环绕的大小为150X150像素,一半就是75。25加50等于75,很简单的数学。所以,当用户拖动达到最大值时,刚好到达环绕的边界。如果你希望拇指杆移动出边界,只需要增加这个值即可。同样,如果你想要让其移动范围更小,减小这个值。

数学层面的问题

这些方法从理论上来看确实很棒,但是要如何真正运用到上述代码中呢?让我们逐步解析每个片段的代码,这样你就能够对真正的流程有更为清晰的认识。首先,你有一套本地存储变量。这些变量涉及到拇指杆和环绕的坐标:

// Store the current x/y of the knob
var _currentX        :Number = _thumb.x;
var _currentY        :Number = _thumb.y;

// Store the registration point of the surrounding ‘joystick holder’
var _registrationX    :Number = _surround.x;
var _registrationY    :Number = _surround.y;

接下来,用环绕的坐标减去拇指杆的坐标,得到的就是存储在控制器内的真正X和Y坐标。

// Subtract the two from each other to get the actual x/y
var _actualX    :Number = _currentX – _registrationX;
var _actualY    :Number =  _currentY – _registrationY;

接下来是_degrees变量。正如我先前提到的,随后我会解释更多的细节。现在你需要知道的就是它能够获取_actualX和_actualY,并将它们转换为弧度(游戏邦注:使用Math.atan()方法),随后再被转化成角度,比如从0到180和从-179到-1这样的值。

_angles变量只是将_degrees值转化回弧度。但是,我希望自己能够简化这段代码,只使用_degrees变量之类的Math.atan()。这样,至少你可以看到角度和弧度之间如何实现转化。

这个过程已几近结束,但是还要说明几点。你需要通过两个部分来限制拇指杆X和Y坐标。首先,你需要计算拇指杆能够移动的最大距离。随后,第二部分是查看拇指杆的X或Y值是否比这个值小。如果它们大于这些值,在超出边界时方法会自动将拇指杆的X或Y坐标限定为最大距离。计算这个距离并不是那么简单。这时要用到三角学。通过计算控制器环绕中的直角三角形两边,你可以很容易地得出斜边的长度。代码如下所示:

// As we want to lock the orbit of the knob we need to calculate x/y at the maximum distance
var _maxX        :Number = Math.round((_radius * Math.cos(_angle)) + _registrationX);
var _maxY        :Number = Math.round((_radius * Math.sin(_angle)) + _registrationY);

正如我说过的那样,第二部分是检测拇指杆的坐标,如果大于最大坐标会被重置为最大值:

// Check to make sure that the value is positive or negative
if(_currentX > 0 && _currentX > _maxX || _currentX < 0 && _currentX < _maxX)
_thumb.x = _maxX;

if(_currentY > 0 && _currentY > _maxY || _currentY < 0 && _currentY < _maxY)
_thumb.y = _maxY;

完成这些代码后,你现在可以对控制器进行测试。现在,它被限制在环绕内,onThumbDrag()方法实现了这一点,所以不会再出现上面移动出边界的情况。

virtual joystick (from flashgen.com)

virtual joystick (from flashgen.com)

拇指的位置

或许你已经注意到,如果你的鼠标移出拇指杆元件,当你释放后,拇指杆并没有回到环绕的中心文职。这是因为,如果你意外将拇指移出环绕范围,那么代码中还没有注册事件来捕捉拇指杆释放的行为。这个问题需要解决,因为你的用户很可能在玩游戏时会将他们的拇指移出拇指杆环绕边界。因为玩家在游戏过程中可能不会盯着控制器,而且控制器是虚拟的,他们无法感受到已经移出边界的肢体反馈。

要解决这个问题,你只需要添加用户释放拇指杆的状态和反应。对于电脑和网页应用来说,它们通常无需支持多点触摸,所以你每次只能用鼠标同单个元素互动。但是,在移动设备上,情况并非如此。因为移动设备上可能存在多点触摸的状况,所以会更加麻烦。

较好的做法是创建隐藏在控制器图像后的不可视区域,监测拇指杆的释放。这样,即便用户将拇指移出拇指杆元件也无关紧要,因为这个区域仍然会对释放做出响应。现在,你需要决定的是这块响应区域的大小。这方面的决定是完全靠主观感觉的,但是如果你能够知道用户会如何拿着设备来使用控制器,那么这算是个不错的开端。

对于新开发者来说,你不需要设置过大的区域,我认为可以限制在不超过屏幕宽度1/3即可(游戏邦注:游戏横向显示)。毕竟,用户需要拿稳设备,所以不会将拇指移动过大的距离。如果对此存在疑虑,可以进行测试直到获得满意的大小。

在以下例子中,我在Flash Professional中将ThumbStick元件进行更新,添加另一个称为“Boundary”的层次,放置一个新的25 X 25环形元件。我为其设置边界成员名称,并确保该层次位于ThumbStick元件所有其他层次之下。

很显然,25 X 25像素的大小并没有多大的作用。但是,在你将元件放入ThumbStick()类中的变量后,你可以很容易地设置其尺寸。这样,你可以很容易地添加设置器,使你可以动态地修改和设置控制器尺寸。在下列代码中,我为这个边界元件的宽度和高度设置了固定数值。

private var _thumb        :Sprite;
private var _surround    :Sprite;
private var _boundary    :Sprite;

private var _degrees    :Number;
private var _radius        :Number = 25;

public function ThumbStick()
{
_thumb = thumb;
_surround = surround;
_boundary = boundary;
_boundary.width = 200;
_boundary.height = 200;

if(Capabilities.cpuArchitecture == “ARM”)
{
_thumb.addEventListener(TouchEvent.TOUCH_BEGIN, onThumbDown);
_thumb.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
_surround.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
_boundary.addEventListener(TouchEvent.TOUCH_END, onThumbUp);
}
else
{
_thumb.addEventListener(MouseEvent.MOUSE_DOWN, onTestThumbDown);
_thumb.addEventListener(MouseEvent.MOUSE_UP, onTestThumbUp);
_surround.addEventListener(MouseEvent.MOUSE_UP, onTestThumbUp);
_boundary.addEventListener(MouseEvent.MOUSE_UP, onTestThumbUp);
}
}

我将边界元件的alpha值设为1。现在再尝试下,即便你的鼠标移出拇指杆,只要仍然位于边界区域内,释放后拇指杆就会重新回到中心。显然,如果你觉得这个边界区域不够大,你可以根据自己的需要进行调整,甚至可以改变其形状,用矩形也可以。

virtual joystick (from flashgen.com)

virtual joystick (from flashgen.com)

总结

在这片文章中,你掌握了如何创建可用于触屏设备的虚拟控制器的基础。你还知道了如何将拇指杆的移动限制在控制器环绕内,以及当用户意外将拇指移出控制杆时并释放时如何让其回到中心位置。

有了这些基础知识,你就可以进入更深的层次,比如当用户移动控制器时分派事件,这样你就能够更容易地将虚拟控制器添加到游戏中。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

virtual controllers for touch based devices (pt.1)

Mike Jones

As I’m working on a series of articles on Pushbutton Engine I thought I’d use that as a basis for testing some virtual controllers I have be playing about with. For one, it saves me for having to create the majority of the scaffold code and concentrate on the actual controllers and how they integrate. One thing I was acutely aware of was keeping the integration of the controller as generic as possible with the idea that it could be used in any game, regardless of the code that supports it.

I also made a few assumptions, firstly I used my thumbs as the basis for determining the size of the controller’s thumb stick ‘head’ – I have quite big thumbs so I assumed that this would suffice for most people. The second one was more technical. As most PC games tend to rely heavily on keyboard input (and Flash web games even more so) for controlling your ship / player; I opted to emulate the 4 arrow keys – Up, Down, Left and Right instead of using co-ordinate translation. There were a few reasons for key input emulation over co-ordinate translation. The first one I have already outlined. The second was to do with ease of conversion; as most Flash games use the keyboard already adding in the virtual controller shouldn’t require a major update to any pre-existing code. Lastly, there is the testing and debugging.

Designing The Controller

The first thing to consider is the actual visual appearance of your controller – in my case this was going to be a thumb stick like those found on the Playstation or XBox controllers. For ease of mapping the users touch input on the thumb stick you really need two elements. First the thumb area, i.e. the place where the user places their thumb; secondly you need a surround. The surround is useful for two reasons. It provides a visual indication to the user on how far the thumb stick moves as well as allowing you to use this as the boundaries of your entire controller for the purpose of determining where the users thumb actually is and more importantly when they lift their thumb up. You can see a simple design for the entire thumb stick controller (including both the thumb and surround) in the image on the right.

Interestingly you don’t actually need to have any visuals if you don’t want them. Some games forego them and just map the users thumb location. This can be confusing for some so depending on type of users you are expecting to attract to your game may determine whether you can remove the visual elements entirely. If in doubt make the controllers slightly transparent.

As their is a visual aspect to this controller it makes sense to leverage Flash Professional CS5 – you can do this all in Flash Builder if you wish, but it makes the initial set up easier using a more visual tool. Create a new FLA file and save it somewhere on your machine with the file name VirtualControllers. Next create a new ActionScript 3.0 class, call it ThumbStick.as and save it in the same directory as the one that contains VirtualControllers.fla. I use a package structure for my classes so I have nested folders as the image shows. You don’t have to do this if you don’t want to and in this article you’ll be using a flat package structure.

Before we start writing code though you need to create a symbol in the Library for the controller, and import the image assets for both the surround and the thumb stick (which you can download via the link at the bottom of this article, or create your own).

Create a new symbol (Insert > New Symbol…)

Call this new symbol ThumbStick

Locate the downloaded assets for the surround and the thumb stick and import them in to your library (File > Import > Import to library)

Flash Pro CS5 will automatically create two new symbols (Symbol1 and Symbol2) in your Symbol library

Open the Symbol library (Window > Library)

Select Symbol1 and rename it based on the graphic it contains (it’ll either be the surround or the thumb image)

Repeat this process for Symbol2

Once you have renamed the symbols you need to edit them both to make sure that the enclosed image is centred in the middle of the symbol, as well as making sure that they are set as MovieClip symbols not Graphic symbols (which is what they will be by default).

Right click on the Surround symbol in your library

Select Properties from the context menu

When the properties dialog opens change the top Type drop down from Graphic to MovieClip

Click OK to apply and close this dialog window

Repeat this for the Thumb symbol too

With the Thumb symbol still selected double click it to enter edit mode

The stage should update with the image asset placed approximately in the middle of the screen

Select the image (a bounding blue box should appear)

From the Properties panel (Window > Properties) set the X and Y values to -45, this will centre the Thumb image as it’s 90×90 pixels

Repeat this for the Surround symbol, except set the X and Y values to -75 as the Surround image is 150×150. Again this will centre it

Now you have the main elements of your Thumb Stick controller defined you’ll need to combine them. To do this first edit the symbol you created called ThumbStick

Double click on the ThumbStick symbol in your Library to enter edit mode

Rename the default layer in your Timeline (Window > Timeline) to Thumb by right clicking on the layer and selecting Properties from the context menu

A dialog will open where you can set the new layer name. You can also just double click the layer to edit it directly

Create a new layer in the Timeline panel (click the New Layer icon in the bottom left of the Timeline) and name it Surround

Make sure you still have the Surround layer selected

Drag the Surround symbol from your library and make sure it is centred in the middle of your symbol

With the Surround symbol still selected open the Properties panel and give it an instance name of surround

Once you are happy with its position select the Thumb layer

With the Thumb layer selected drag the Thumb symbol on to the stage of the ThumbStick symbol, making sure it’s on the Thumb layer

Again centre the Thumb symbol so it is in the exact centre (note the cross hair in the middle of the symbols in the image)

And as with the Surround symbol, open the Properties panel (making sure it is still selected) and give the Thumb symbol the instance name of thumb

Quick Tip

If the bitmap assets are looking a bit ‘jaggy’ you can always apply smoothing to them by right clicking on each image in the library and selecting Properties from the context menu. When the dialog opens just tick the Smoothing checkbox and click OK to apply.

Now you have the FLA and ActionScript files saved we can get about creating the initial code. The first thing you need to do is make sure your class extends Sprite (by default it doesn’t extend anything). You’re also going to need some private variables to store some of the controllers info in. To that end add the following (or just copy and paste the code below):

Defining The Controller

Now that you have a design, and you have created your actual controller symbol, you need to start thinking about adding the code to actually control it when the user interacts with it. The best approach is to break this process down initially as a bulleted list of its functional parts. The list will likely look something like this:

Move thumb stick in a circular motion

Limit thumb stick movement to within the surround

Return thumb stick to centre of controller when the user removes their thumb

Dispatch an event when the user moves the thumb stick

Obviously you are free to add more items to the list but these four cover the basic functionality. With that in mind lets start coding…

Writing The Code

The first thing you need to do is make sure that your ThumbStick class extends Sprite – by default Flash Professional creates non-inheriting classes. Now your code will look like this (I’ve applied a bit of formatting to my structure in case yours is formatted slightly differently):

Looking back at the four functional points I laid down earlier, the first related to moving the thumb stick in a circular motion let’s start by moving the thumb stick. This is a fairly complex process so I’m going to save that for part 2 of this article. However what you are going to do is set the controller so when you press down on the thumb stick it will drag in the direction of your thumb movement and on release it will re-centre itself back to the middle of the surround.

To do this you need to add a couple of event listeners. These will deal with responding to the touch events when the user presses down with their thumb as well as when they remove it. Even though you can directly interact with the symbols you have within your controller (as you gave them instance names), the code hinting in Flash Professional’s code editor doesn’t kick in – so I tend to make a private variable that I map to the symbol to make this easier.

The code below has the handlers included as well as the event listeners so you can see the flow and structure (there is a problem with this code and I’ll explain what it is in a moment):

So what’s the problem then? Well it has to do with testing. You see if you run this from inside Flash Professional you won’t be able to test the TouchEvents as there is no equivalent on a normal desktop PC. Not particularly useful if you don’t have a physical device at hand to test on (at this point I will emphasise how important it is to test you games, applications etc on the target hardware at some point).

One approach is to check the type of hardware you are running on and adjusting the code flow based on that by using the Capabilities() class. So if I update the code above further I can test to see if this is running on an ARM based processor or not (i.e most Smart phones and tablets). I can then tailor how the code is initialized.

This is useful, but I tend to remove all of the non essential code just before I test on the actual device – after all why include code you will never execute? Moving on…

THe next functionality you need to implement is the actual ability to move the thumb stick. This is pretty easy to do and I’ve just created two methods to control this – one to deal with the actual movement when the user is touching the thumb stick, and one to reposition the thumb stick back to the centre of the surround:

To access these methods you need to place a call to initDrag() inside your event handlers that respond to the user placing their thumb on the actual thumb stick – onThumbDown() and onTestThumbDown(). Likewise the resetThumb() method needs to be called when the user removes their thumb from the controller, so you need to add a call to it within the onThumbUp() and onTestThumbUp() methods.

If you test it now it should function in a similar manner to the example below. Clicking and dragging the thumb stick moves it (try the example below). Releasing the mouse button will centre it back to its original position in the middle of the surround – note that while you can click and drag the thumb stick with your mouse, but it isn’t confined by the boundaries of the surround. This isn’t exactly what we want. Worry not though, you’ll be addressing that next.

Setting Controller Boundaries

As I mentioned earlier, this gives you basic drag functionality within your virtual controller and repositions the thumb stick on releasing the mouse button. However it doesn’t restrict the movement to within the actual controller surround. So with out further ado let’s jump right in the deep end and start with some Trigonometry :p. I’m not going to give you a trig primer here, but I will explain where applicable what is actually happening – if you want to know more there are many good resources online (a simple google search should suffice)

The first thing you need to do is add in a new event listener to monitor the thumb stick as you move it so it can be restricted when it hits the desired position at the boundaries of the controller surround. Now, normally you’d probably use the MouseEvent.MOUSE_MOVE event to monitor the movement of the thumb stick. However this event is quite expensive in terms of event dispatching and usage compared to the Event.ENTER_FRAME event. With that in mid you need to add the following line to end of the initDrag() method:

Likewise you need to remove this event when the user stops interacting with the controller. To achieve this you just need to remove the event listener in the resetThumb() method. Like the initDrag() method add the following line to the beginning of the resetThumb() method. That way the event is removed straight away and any other house keeping routines can be processed once this has happened:

You’ll notice that both lines of code refer to an as yet unimplemented method called onThumbDrag(). This method is where all of calculations relating to the thumb stick’s movement boundaries are set. Below is the entire onThumbDrag() method code you’ll need to implement in your code.

Before I go through the various aspects of this method you’ve probably noticed that there are two variable fields being referenced beyond all of the local variables. They are _degrees and _radius. Both of which are defined below

The _degrees variable I’ll cover later on, but the _radius variable deals with the amount of movement within the boundary of your controller. The reason it is set to 25 here is that the thumb stick is 50×50 pixels therefore half of that is 25 and the surround is 150×150 pixels – again half of that is 75. If you add 25 to the thumbs 50×50 dimensions you 75. Simple really. So when the user drags it the most it can move is to the actual edge of the surround. If you want it to move beyond the edge slightly just increase the value. Likewise if you want it to move a shorter distance reduce this amount.

The Math ‘Bit’

That’s all well and good in theory, but how does that actually get applied in the code above? Well let’s start at the beginning of the method and look at each block of code so you can get a clearer understanding of the actual flow. First up you have a set of local storage variables. These just contain references to the position of the thumb stick and the surround:

Next up are the variables that store the actual X/Y position within the controller itself by subtracting the thumb stick’s position from the surround’s position.

Next up is the _degrees variable. As I mentioned earlier I’ll be going in to more detail on what you’ll be using this for shortly. All you need to know at this point is that it takes the values of _actualX and _actualY and converts them to radians (using the Math.atan() method) which are them in turn converted in to degrees – i.e. a value from 0 to 180 and -179 to -1

The _angles variable just converts the _degrees value back in to radian. While I appreciate that I could have simplified this code and just used Math.atan() like the _degrees variable. This way you can at least see how to convert between degrees and radians (should you ever need to)

You’re almost at the end of this process so hang on in there. There are two parts to restricting the thumb sticks X and Y position. First of all you need to calculate the maximum distance the thumb stick can move. The second part then checks to see if the thumb stick’s actual X or Y values are within or beyond those values. If they are beyond those values it automatically sets the X or Y of the thumb stick to the maximum distance for that property if outside of the boundary. To calculate the distance isn’t that obvious as you are measuring the edge of a circle. That’s where the trigonometry I mentioned comes in. By calculating the two sides of a right angle triangle within the controller surround you can easily then work out the hypotenuse (longest side) of the triangle from the centre. And that is all that is happening here:

As I said the second part then actually checks the position of the thumb stick and if it is beyond the maximum position it resets it to that maximum:

With this code in place you can now test your controller. Now it is restricted to the orbit that defined within the onThumbDrag() method, so it doesn’t move beyond the bounds of the actual surround just like the version below

Where’s The Thumb Again?

One thing you will have already noticed is that if your mouse moves outside of the thumb stick symbol, when you release it, the thumb stick doesn’t return to the centred position in the middle of the surround. This is because there is no event registered to capture the release of the thumb stick if you accidentally move out of it. This needs resolving as it is highly likely that your user will move their thumbs beyond the bounds of the thumb stick when playing your game (it’s just one of those things as they aren’t looking at the controller and as it’s virtual there isn’t any real feedback that they have extended beyond the desired bounds).

To fix this you could just register the stage and respond when the users releases the thumb stick. This is fine desktop / web apps as they don’t generally support multi-touch so you can only interact with one element at a time with the mouse. On devices this isn’t the case. Because you can have multiple touch points if you listened for the release via the stage if you have a virtual button – for firing your ships guns for example – then every time you lifted your finger off the button it would reset the thumb stick. Not ideal really.

A better way would be to create a invidsible area that sits behind the actual controller graphics and listens for the release. That way it doesn’t matter if the user moves off the actual thumb stick symbol as this area would still respond to the release. Now one thing you would need to decide upon if you chose to implement this is how big should this ‘run off’ area be? Well that can be a bit subjective, but if you think about how the user will hold their device to use the controller then that should give you a good starting point.

For starters you don’t need it to cover a massive area – if fact I would say no more than a third of the width of the screen (working on the premise that the game is probably displayed as landscape). After all it’s unlikely the user will move their thumb a big distance given that they will be holding the device at the same time. If in doubt, test it out until you get a happy medium.

In the example below I have updated the ThumbStick symbol in Flash Professional by adding another layer called ‘Boundary’ and placed a new 25×25 pixel filled (red) circlular symbol in it. I gave it the instance name of boundary and made sure the layer was underneath all of the other layers in the ThumbStick symbol.

Obviously 25×25 pixels in size isn’t going to be that useful. However once you map the symbol to a variable in your ThumbStick() class you can easily set its dimensions. That way you could easily add in a getter/setter that would allow you to dynamically set the dimensions of the controller. In the code below I’ve just set this boundary symbol’s width and height with hard values within the constructor.

I’ve left the boundary symbols alpha value at 1 so you can see it (as shown in the SWF below – it’s the red circle :p). Try it out thius time and even though you may drag off the actual thumb stick, as long as ou are still over the boundary area it will still reset the thumb stick back to the centre once released. Obviously if the boundary area isn’t big enough you can enlarge it as you feel is necessary – it doesn’t even need to be circular. A rectangle would work just as well.

Summary

In this article you got a good foundation in how to approach creating a virtual controller that can be used on touch based devices (and we’ll be looking in to that part of the process in more detail in part 2). You also saw how to restrict the movement of the thumb stick to within the actual controller surround; as well as what should happen if the user accidentally moves their thumb off of the thumb stick and then removes their thumb from the screen.

With the foundation set you can now leverage this to add further enhancements such as dispatching events when the user moves the controller so that you can wire it in to your game easily. (Source: blog.flashgen.com)


上一篇:

下一篇: