导读:我觉得自己可以使用Pushbutton Engine作为基础来测试某些曾经玩过的虚拟控制器,所以编写了这个有关Pushbutton Engine的系列文章。使用这种方法使我无需将大量时间投入 到代码编写中,能够更专注于真正的控制器及其整合方法。我真切地感受到,要让这种控制器整合尽量标准化,这样才能够被运用到任何游戏中,无需考虑代码的影响。
我还做了些许假设,首先我用自己的拇指来决定控制器拇指杆的大小。我的拇指很大,所以我认为这样的大小应该能够满足多数人的需求。第二个假设显得更为技术化。对于船只 或角色的控制而言,多数PC游戏对键盘输入有很强的依赖(注:Flash网页游戏尤为突出),所以我试图模拟上、下、左和右4个方向键,而不是使用坐标转换。使用按键输 入模拟取代坐标转换的原因很多。上述是第一个原因。第二是为了转换的便利性,因为多数使用键盘的Flash游戏已经添加了虚拟控制器,这样就无需对现有代码进行大幅更新。最 后,采用这种方法有利于测试和调试。

virtual joystick
设计控制器
首先要考虑的是控制器的视觉外观,本文将使用的是类似于Playstation或XBox控制器上的拇指杆。为了让用户更容易地使用拇指杆进行触碰输入,你需要两个元素。第一个是拇指 区域,也就是用户放置拇指的地方,第二个元素是环绕。使用环绕有两个原因,既可以在视觉上让用户明白拇指杆移动的范围,也可以用次来作为整个控制器的边界,这样你就能 够知道用户拇指的位置,更重要的是能够知道他们的拇指何时离开控制杆。下图便是整个拇指杆控制器(注:包括拇指区域和环绕)的样式。
有趣的是,如果你不想要视觉呈现,你完全可以将其移除。有些游戏隐藏了拇指杆的呈现,只向用户标注拇指可放置位置。这种方法可能会让部分玩家感到困惑,所以是否完全移 除视觉元素的决定应当取决于游戏的目标用户类型。如果不确定,可将控制器设置为透明风格。
因为这属于控制器的视觉层面,所以可以使用Flash Professional CS5来制作。你也可以使用Flash Builder来完成所有的工作,但是使用更具视觉化的工具会让初始化更加简单。 创建新FLA文件,命名为VirtualControllers,将其保存在电脑上。接下来,新建ActionScript 3.0类,命名为ThumbStick.as,将其保存在与VirtualControllers.fla相同的目录 下。我使用程序包结构来呈现类,所以文件夹就如下图所示。如果你不喜欢这种方法,也可以不这么做,但我们在本文将使用略显单调的程序包结构。

folder hierarchy
在我们开始编写代码钱,你需要在库中为控制器创建元件,导入环绕和拇指杆图片资产(注:可以下载作者的设计,也可以自行设计)。
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的元件:
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()方法中添加对这项命令的调用。
如果你现在进行测试,它的功能应当同以下范例类似。试着点击和拖动以下拇指杆。释放鼠标左键后,它会回到环绕中心的原位置上。应当注意的是,尽管你可以用鼠标点击和拖 动拇指杆,但其并不受环绕边界的限制。这不是我们想要的结果。不过不用担心,接下来我们就要处理这个问题。
设置控制器边界
正如我之前提到的,现在我们已经实现了虚拟控制器的基本拖动功能,而且在释放鼠标后拇指杆会重新回到原位置。但是,它的移动并没有被限制在控制器环绕内。接下来,我们 要处理的就是这个问题。
首先,你需要做的是添加新事件监听器,在移动拇指杆时对其进行监管,这样当它移动到控制器边界时就会受到限制。现在,通常情况下你可能会使用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()方法实现了这一点,所以不会再出现上面移动出边界的情况。
拇指的位置
或许你已经注意到,如果你的鼠标移出拇指杆元件,当你释放后,拇指杆并没有回到环绕的中心文职。这是因为,如果你意外将拇指移出环绕范围,那么代码中还没有注册事件来 捕捉拇指杆释放的行为。这个问题需要解决,因为你的用户很可能在玩游戏时会将他们的拇指移出拇指杆环绕边界。因为玩家在游戏过程中可能不会盯着控制器,而且控制器是虚 拟的,他们无法感受到已经移出边界的肢体反馈。
要解决这个问题,你只需要添加用户释放拇指杆的状态和反应。对于电脑和网页应用来说,它们通常无需支持多点触摸,所以你每次只能用鼠标同单个元素互动。但是,在移动设 备上,情况并非如此。因为移动设备上可能存在多点触摸的状况,所以会更加麻烦。
较好的做法是创建隐藏在控制器图像后的不可视区域,监测拇指杆的释放。这样,即便用户将拇指移出拇指杆元件也无关紧要,因为这个区域仍然会对释放做出响应。现在,你需 要决定的是这块响应区域的大小。这方面的决定是完全靠主观感觉的,但是如果你能够知道用户会如何拿着设备来使用控制器,那么这算是个不错的开端。
对于新开发者来说,你不需要设置过大的区域,我认为可以限制在不超过屏幕宽度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
我们设置完“逃跑”轨迹,剩下的就是调遣事件,让“外部世界”知道控制器的具体操作内容。就如我在文章第一部分中提到的,我选择将键盘投射至控制器中,因为多数网页 Flash游戏主要着眼于键盘控制,而非鼠标移动(注:有些情况下,这和Flash运行时间没有提供Mouselook支持有关)。
目前这听起来像是个简单过程。毕竟只有4个方向键,所以我在控制器上将其设置成上下左右(Up、Down、Left和Right)。如果我只是想进行4个方向的操纵杆控制(想想《Dig- Dug》或《Mr. Doo》),这完全没有问题。但多数现代平台游戏都至少支持8个方向的移动操作。这带来一定问题,因为这意味着你需要设计按键操作组合——例如,左上或右下。若是采用实际键盘,这完全不费吹灰之力,因为用户可以组合按下这些按键,它们会给予相应回应。我们需要效仿这一模式,这意味着你需要进行更多设置,确定你的虚拟控制器 要如何通过模拟按键操作进行调遣。
映射按键
首先要做的就是,投射4个方向键。这样你就清楚自己是基于操纵杆的位置调遣事件。要完成这一操作,你需要添加简单的内部循环(或是标记),这会更新有关按键输入模拟的信 息。所以如果你要进行的是推向顶部的操作,那么你多半会调遣代表按下“上”按键的事件。
反向控制器
在未来的逐步发展中,你会想要添加的一个设置是,让用户颠倒控制的Y轴(上/下)。这非常适合基于纵向和横向路径飞行的太空飞船游戏。因此和真实飞机一样,按下控制器的 “上”按键会促使太空船向下移动,按压控制器的“下”按键会令其向上移动。
你需要通过另一方式检查和调遣模拟按键操作。所以你需要创建新的dispatchKeyCombo()保护方式,在onThumbDrag()方法的底部将其设置成调用状态。代码内容应如下(注意:出 于简洁,我移除onThumbDrag()方法中的多数代码内容):
protected function onThumbDrag(e:Event):void
{
// Additional code removed for brevity
dispatchKeyCombo();
}
protected function dispatchKeyCombo():void
{
}现在每次用户移动操纵杆及onThumbDrag()方法被调用时,dispatchKeyCombo()方法也会自动被调用。设置完存根方法后,下面就来着手添加初始逻辑和事件调遣。首先需要进行的 操作是,判断操纵杆是否同边缘存在联系。你可以在onThumbDrag()方法进行调用,这里你有个分层级存储当前操纵杆积极或消极位置的变量_degrees。此前我略过它的具体运用,称后面会进行详述。现在是时候了。你需要层级数值,这样你才能够以当前的层级旋转为基础,将操纵杆的位置投射到方向键。
记住现在你只是添加4个方向的操纵杆支持。要完成这一操作,你需要查看_degrees变量的数值,若这相当于4个方向(上下左右)的必要求值,那么你就可以调遣事件。根据当前 设置,4个操纵杆位置的层级数值分别是:右=0,下=90,左=180,下=-90。把握具体数值后,插入逻辑条件,确定需要调遣的数值则就非常简单。
protected function dispatchKeyCombo():void
{
// Thumb stick position – Right
if(_degrees == 0)
{
trace(“Right”);
}
// Thumb stick position – Down
else if(_degrees == 90)
{
trace(“Down”);
}
// Thumb stick position – Left
else if(_degrees == 180)
{
trace(“Left”);
}
// Thumb stick position – Up
else if(_degrees == -90)
{
trace(“Up”);
}
}从理论上来说,这就是你需要进行的操作。但若你在调试模式中进行测试,你会发现要在移动操纵杆时达到特地数值非常困难,因为你需要非常准确——这通过鼠标很难完成,想 想如果你要通过自己的拇指,这将会多么棘手。最显而易见的做法就是,通过系列数值判断操纵杆的位置。这样你就创建同上下左右相关的“区域”。下面是通过区域判断操纵杆 位置的更新代码。注意,这里的条件更加复杂,因为我们主要判断操纵杆是否介于数值之间(有时候也需要处理积极和消极数值)。
protected function dispatchKeyCombo():void
{
// Thumb stick position – Right
if(_degrees >= -22 && _degrees <= 22)
{
trace(“Right”);
}
// Thumb stick position – Down
else if (_degrees >= 68 && _degrees <= 112)
{
trace(“Down”);
}
// Thumb stick position – Left
else if((_degrees >= 158 && _degrees <= 180) || (_degrees >= -179 && _degrees <= -158))
{
trace(“Left”);
}
// Thumb stick position – Up
else if(_degrees >= -112 && _degrees <= -68)
{
trace(“Up”);
}
}如果你在Debug模式中测试这一版本,那么你会看到追踪轨迹更加连贯——显然你可以调节值域,改变操纵杆输出值的准确度。但由于你所瞄准的是触控设备,所以不要设定具体值 域,用户的拇指通常不是那么准确(注:特别是当他们处在激烈炮战中的时候)。
现在你已设定基本结构,可以着手下个步骤。将键盘输入值投射到操纵杆位置的对应数值。ActionSCript提供各普通按键数值的常量,这使得执行输入内容变得非常简单。在你将 任何按键输入代码添加至dispatchKeyCombos()方法前,你需要定义4个新变量。这些属于实体变量,而非方法变量,所以不妨将它们置于类顶部的其他实体变量之下。
private var _primaryKeyCode :int;
private var _secondaryKeyCode :int;
private var _previousPrimaryKeyCode :int;
private var _previousSecondaryKeyCode :int = 0;目前你无需担心这块内容;我会在添加额外功能时解释它们的用途。现在你唯一需要关心的内容是_primaryKeyCode,你需要在此变量中存储当前按键代码。
首先,将各trace()陈述替换成_primaryKeyCode变量的引用。根据自己的检测方向,你可以就相关按键输入常量进行调遣。当这些操作完成后,你只需要调遣_primaryKeyCodeas的 更新数值,这是新KeyboardEvent()的键码属性,就如如下代码块所示:
protected function dispatchKeyCombo():void
{
// Thumb stick position – Right
if(_degrees >= -22 && _degrees <= 22)
{
_primaryKeyCode = Keyboard.RIGHT;
}
// Thumb stick position – Down
else if (_degrees >= 68 && _degrees <= 112)
{
_primaryKeyCode = Keyboard.DOWN;
}
// Thumb stick position – Left
else if((_degrees >= 158 && _degrees <= 180) || (_degrees >= -179 && _degrees <= -158))
{
_primaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Up
else if(_degrees >= -112 && _degrees <= -68)
{
_primaryKeyCode = Keyboard.UP;
}
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _primaryKeyCode));
}更新的虚拟控制器呈现下图样式。(我插入外部事件处理器,这样你就能够在上方的文本框中看到当前的方向输入内容,我还将红色边界的初始值设置成0,这样它就呈透明状态) 。

当前方向
扩展范围
现在你已设定调遣代码的基本结构,下面就来对其进行扩展,这样虚拟控制器代码就能够将控制器变成8个方向的控制器,而不是当前的4个方向模式。鉴于控制器映射的是模拟按 键事件,要在控制中执行附加角度会产生额外要求。
是否还记得我创建的附加变量:_primaryKeyCode和_secondaryKeyCode等?这里你需要用到其中一个,即_secondaryKeyCode。在初始位置中移动操纵杆只需要调遣单个按键事件。 将操纵杆移动到这些离轴角度需要你调遣两个按键事件——例如,若你将操纵杆移至上和右之间的位置,你就需要调遣代表向上箭头键及右箭头键的键控代码。
下面就来看看修改后的代码,同时分析二级事件分配器的添加及针对提供8个方向控制器支持的附加“区域”进行的新检查操作。
protected function dispatchKeyCombo():void
{
_secondaryKeyCode = 0;
// Thumb stick position – Right
if(_degrees >= -22 && _degrees <= 22)
{
_primaryKeyCode = Keyboard.RIGHT;
}
// Thumb stick position – Down
else if (_degrees >= 68 && _degrees <= 112)
{
_primaryKeyCode = Keyboard.DOWN;
}
// Thumb stick position – Left
else if((_degrees >= 158 && _degrees <= 180) || (_degrees >= -179 && _degrees <= -158))
{
_primaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Up
else if(_degrees >= -112 && _degrees <= -68)
{
_primaryKeyCode = Keyboard.UP;
}
// Thumb stick position – Up/Left
else if(_degrees >= -157 && _degrees <= -113)
{
_primaryKeyCode = Keyboard.UP;
_secondaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Down/Left
else if(_degrees <= 157 && _degrees >= 113)
{
_primaryKeyCode = Keyboard.DOWN;
_secondaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Up/Right
else if(_degrees >=-67 && _degrees <= -21)
{
_primaryKeyCode = Keyboard.UP;
_secondaryKeyCode = Keyboard.RIGHT;
}
// Thumb stick position – Down/Right
else if(_degrees >= 23 && _degrees <= 67)
{
_primaryKeyCode = Keyboard.DOWN;
_secondaryKeyCode = Keyboard.RIGHT;
}
if(_secondaryKeyCode > 0)
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _secondaryKeyCode));
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _primaryKeyCode));
}你会发现dispatchKeyCombo()方法出现系列变化。最大变化就是我在此代码块前提到的:我添加围绕操纵杆全方位移动的按键输入映射(注:而非仅仅围绕其进行上下左右 移动)。其次现在出现对变量_secondaryKeyCode的引用。这是应用存储二级按键输入代码的位置。就如我之前提到的,如果你将操纵杆向右上方移动,那么从按键输入角度来看, 你就需要在上和右按键中调遣KeyboardEvent()。在方法初始阶段,将_secondaryKeyCode变量的默认值设置成0,只有当用户将虚拟控制器移至需要两个键码数值的位置时,数值才 会发生更新。
最后一部分是添加检查操作,以查看_secondaryKeyCode变量值是否大于0,如果大于0,就调遣二级KeyboardEvent()。
if(_secondaryKeyCode > 0)
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _secondaryKeyCode));只剩下一块内容,整个方法就大功告成。你也许会发现,你调遣的是KeyboardEvent.KEY_DOWN事件,但你如何让对象关注你的控制器,知道你何时改变方向。你需要调遣 KeyboardEvent.KEY_UP事件。这就引出最后两个变量。它们是:_previousPrimaryKeyCode和_previousSecondaryKeyCode。这些用于存储之前调遣的键码。但为避免调遣不必要的 事件,它们只有在初级和次级键码的新数值发生改变时会被调用,像下面这样:
if(_primaryKeyCode != _previousPrimaryKeyCode)
{
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousPrimaryKeyCode));
_previousPrimaryKeyCode = _primaryKeyCode;
}
if(_previousSecondaryKeyCode != _secondaryKeyCode)
{
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousSecondaryKeyCode));
_previousSecondaryKeyCode = _secondaryKeyCode;
}若用户改变操纵杆的位置,那么相关输入内容的KeyboardEvent.KEY_UP事件就被调遣。由于这一方法包含众多内容,因此我呈现如下完整版本,这样你就能够看到具体操作流程:
protected function dispatchKeyCombo():void
{
_secondaryKeyCode = 0;
// Thumb stick position – Right
if(_degrees >= -22 && _degrees <= 22)
{
_primaryKeyCode = Keyboard.RIGHT;
}
// Thumb stick position – Down
else if (_degrees >= 68 && _degrees <= 112)
{
_primaryKeyCode = Keyboard.DOWN;
}
// Thumb stick position – Left
else if((_degrees >= 158 && _degrees <= 180) || (_degrees >= -179 && _degrees <= -158))
{
_primaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Up
else if(_degrees >= -112 && _degrees <= -68)
{
_primaryKeyCode = Keyboard.UP;
}
// Thumb stick position – Up/Left
else if(_degrees >= -157 && _degrees <= -113)
{
_primaryKeyCode = Keyboard.UP;
_secondaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Down/Left
else if(_degrees <= 157 && _degrees >= 113)
{
_primaryKeyCode = Keyboard.DOWN;
_secondaryKeyCode = Keyboard.LEFT;
}
// Thumb stick position – Up/Right
else if(_degrees >=-67 && _degrees <= -21)
{
_primaryKeyCode = Keyboard.UP;
_secondaryKeyCode = Keyboard.RIGHT;
}
// Thumb stick position – Down/Right
else if(_degrees >= 23 && _degrees <= 67)
{
_primaryKeyCode = Keyboard.DOWN;
_secondaryKeyCode = Keyboard.RIGHT;
}
if(_primaryKeyCode != _previousPrimaryKeyCode)
{
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousPrimaryKeyCode));
_previousPrimaryKeyCode = _primaryKeyCode;
}
if(_previousSecondaryKeyCode != _secondaryKeyCode)
{
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousSecondaryKeyCode));
_previousSecondaryKeyCode = _secondaryKeyCode;
}
if(_secondaryKeyCode > 0)
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _secondaryKeyCode));
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, _primaryKeyCode));
}到目前为止,具体例子都在浏览器或桌面测试过,但我们的目标是让此控制器能够在触控装置上运作。也就是说,如果你现在将此控制器放入设备中,它将无法运作。
为什么?因为你需要告诉游戏,控制器执行多点触控事件,这样它才知道如何进行操作。现在你可以将这些代码放入虚拟控制器中——确保执行多点触控事件,或者你可以将其具 体化,放入实际游戏中。就个人而言,我倾向将此放入控制器的构造函数中,主要因为在编码集中阶段,我常常会忘记植入多点触控代码,然后在发现其无法于设备上运作时再匆 忙进行设置。显然如果有多个控制器,这些操作会有些重复,但这些代码并不多,完成初始化后,再次进行设置不会产生什么额外费用。
所以最后一个步骤就是将这些代码粘贴至你虚拟控制器的构造函数中(注:就在你添加事件处理程序的位置上方)中,然后就大功告成。
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;值得一提的是,在于屏幕检测点击和放开操作时,你需要利用MultitouchInputMode.TOUCH_POINT事件。若你需要追踪各种输入内容,以执行各种手势,那么你就需要利用 MultitouchInputMode.GESTURE事件。关于控制器,我建议你不要将其纳入其中,否则将产生负面影响。
内容梳理
最后你需要做的是,整理系列松散的字符串。虽然现在如果你将拇指移开控制器,控制器会调遣正确事件,但它们会持续处于调遣状态中。原因很简单,在用户将拇指移开控制器 时,全类型事件不会终止其他所有事件。这个问题很容易解决。只需创建新方法killAllEvents(),在其中植入如下代码:
protected function killAllEvents():void
{
if(_primaryKeyCode)
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousPrimaryKeyCode));
if(_secondaryKeyCode > 0)
dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, _previousSecondaryKeyCode));
}在resetThumb()方法中将其设置成调用状态,就在你移除事件处理器之后。你的resetThumb()方法应该呈此状态:
protected function resetThumb():void
{
removeEventListener(Event.ENTER_FRAME, onThumbDrag);
killAllEvents();
_thumb.stopDrag();
_thumb.x = 0;
_thumb.y = 0;就是这样。若你现在进行测试,你会发现,当操纵杆以任何方向移动时,事件就会被调遣,如果你的拇指从控制器上移开,事件就停止传播。
总结
在这部分文章内容里,我们主要谈论如何轻松通过虚拟控制器模拟键盘输入,然后进行调遣,这样任何注意这些事件的外部对象就能够在接收到事件时做出反应。
相关阅读:
盛大游戏完成私有化 19亿美元卖给凯德集团http://www.sfw.cn/xinwen/465830.html
运营惨淡吃不消?万代南梦宫宣布下架8款游戏http://www.sfw.cn/xinwen/465831.html
天润控股8亿收购点点乐遭质疑 股票交易异常波动http://www.sfw.cn/xinwen/465856.html
