Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wiimote support #1563

Merged
merged 21 commits into from
Jul 15, 2015
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
eb7764b
Adding Wii Remote support (with DolphinBar)
larsiusprime Jul 11, 2015
fa6be21
Working on more gamepad wiiu stuff
larsiusprime Jul 13, 2015
9c449d9
input/gamepads: WiiRemote - Fix inverted dpad
larsiusprime Jul 14, 2015
c554014
input/gamepads: remove speculative inputs that none of my hardware ev…
larsiusprime Jul 14, 2015
8f46b51
Merge branch 'dev' of https://github.com/HaxeFlixel/flixel into wiimote
larsiusprime Jul 14, 2015
81ccbc7
input/gamepad: Removed PS3 class, because there are no default driver…
larsiusprime Jul 14, 2015
843fe13
FlxGamepad: removed trace code
larsiusprime Jul 14, 2015
ab1c615
input/gamepads: PR cleanup
larsiusprime Jul 14, 2015
8c38d80
input/gamepad -- added SUPPORTS_POINTER checks, more formatting cleanup
larsiusprime Jul 14, 2015
7fcf6ef
input/gamepads: cleanup
larsiusprime Jul 14, 2015
2a567f8
input/gamepad: trash debug code
larsiusprime Jul 14, 2015
5bf32dd
input/gamepad: fix toString() for FlxGamepad
larsiusprime Jul 14, 2015
03312ed
input/gamepad -- documentation for gamepad.pointer
larsiusprime Jul 14, 2015
3b7d6e8
input/gamepad : compile fix
larsiusprime Jul 14, 2015
9ac90d6
input/gamepad: documentation
larsiusprime Jul 14, 2015
4c4c89f
input/gamepad: (fix) motion should always be -1 to 1 range on all tar…
larsiusprime Jul 14, 2015
ea289b1
input/gamepads: added documentation, cleaned up y axis inversion for …
larsiusprime Jul 14, 2015
5bb1b86
input/gamepad ... only getting inverted y axis on standalone windows/…
larsiusprime Jul 14, 2015
a56fda4
input/gamepads documentation for FlxAnalogToDigitalMode
larsiusprime Jul 14, 2015
522b957
input/gamepad: cleanup
larsiusprime Jul 14, 2015
244360b
inpuput/gamepad: disable JS #ifs to get it to compile
larsiusprime Jul 15, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 204 additions & 16 deletions flixel/input/gamepad/FlxGamepad.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import flixel.input.FlxInput.FlxInputState;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.gamepad.id.FlxGamepadAnalogList;
import flixel.input.gamepad.id.FlxGamepadButtonList;
import flixel.input.gamepad.id.FlxGamepadMotionValueList;
import flixel.input.gamepad.id.FlxGamepadPointerValueList;
import flixel.input.gamepad.id.WiiRemoteID;
import flixel.math.FlxPoint;
import flixel.math.FlxVector;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxStringUtil;
import flixel.util.FlxTimer;

#if FLX_GAMEINPUT_API
import flash.ui.GameInputControl;
Expand All @@ -32,6 +36,12 @@ class FlxGamepad implements IFlxDestroyable
*/
public var model(default, set):FlxGamepadModel;

/**
* For gamepads that can have things plugged into them (the Wii Remote, basically).
* Making the user set this helps
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"helps..." what?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, I did forget to finish that didn't I.

*/
public var attachment(default, set):FlxGamepadModelAttachment;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... in theory, a gamepad could have multiple attachements?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, yes; but let's wait until we actually have a use case for that ... YAGNI and all that.

Unless you can think of something super obvious that we could test with.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this was more of a general question rather than "this needs to be done differently".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure NP! But I'm also super interested to know if you have a game controller like that in mind; I'm already thinking about other stuff we could do with attachments -- X360 has the microphone/headset for instance, but not sure if we have any chance of exposing API's for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, wasn't thinking about any controller in particular.


/**
* The gamepad model this gamepad has been identified as.
*/
Expand Down Expand Up @@ -63,6 +73,14 @@ class FlxGamepad implements IFlxDestroyable
* Helper class to get the justMoved, justReleased, and float values of analog input.
*/
public var analog(default, null):FlxGamepadAnalogList;
/**
* Helper class to get the float values of motion-sensing input, if it is available
*/
public var motion(default, null):FlxGamepadMotionValueList;
/**
* Helper class to get the float values of mouse-like pointer nput, if it is available
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nput? :)

*/
public var pointer(default, null):FlxGamepadPointerValueList;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense to use a FlxPointer for this instead? That's the base class for FlxMouse and FlxTouch -> consistent API.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah probably!

As a quick aside -- is it possible to override Flixel's mouse input with another source, so you could use a gamepad analog stick, or wiimote pointer input, or even keyboard presses, etc, to drive the mouse, rather than native mouse input? Would be super swell for accessibility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually looking at it I'm not sure. As for right now I'm just spitting out the raw analog output from the controller, and there's a whole bunch of uses for it. I think ONE of those uses is definitely to map that to the screen coordinates (which seems to be FlxPointer's purpose), but the game designer / player could also just want the raw analog input for something else.

I'd love the option to map it to the screen but I don't want to over-process the data before the user has access to it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the values already be screen coordinates, and be mapped to world coordinates? It's not like the controller knows about flixel's coordinate system.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't you add setGlobalScreenPositionUnsafe() for having a different source?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blame history might not show that because FlxPointer didn't exist back then, but I'm sure you added it specifically for flixel-ui's custom cursor stuff.

That seems a bit misleading then? The usage of "pointer" led me to think that these coords are comparable to the x/y of the mouse / a touch. This at least needs to be documented properly - I don't think the [0, 1] range is mentioned anywhere either?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, this will take some thinking.

So, we don't have the consoles hooked up yet, but here's every device that might use the "gamepad.pointer" API:

  • Nintendo 3DS
  • Nintendo WiiU
  • OUYA touchpad on controller
  • PS4 touchpad on controller
  • Steam controller (multiple touchpads)
  • Wii remote (IR camera)
  • PSVita (has a front AND back touchpad, though nobody really uses the back)

So the obvious super easy thing to do on the native console side (or even PC if we can manage) is to rig up the input from the touchscreen or other "pointery" gamepad input to Lime/OpenFL's mouse/touch events, report absolute world coordinates, and call it a day. Flixel doesn't even need to "know" about this.

The problem with this solution is that you might have more than one device that has this kind of "pointery" input, and the mouse/touch API is built around the paradigm that there is ONE mouse/touch surface on the device/computer. So if we do that with the API, then you can't, say, make a PS4 game where everyone is using their own touchpad. (Or a game on PC with multiple Steam controllers that does the same).

On the 3DS obviously we should just use the same touch API we use for mobile. On the WiiU -- can you have more than one gamepad active on the same console?

So there's this issue to work out.

Other than that, what do you propose besides "gamead.pointer" as the name?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointer might be fine (unless you have a better name, I don't right know), as long as it's documented what the values mean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's go with that for now. Where should I put the documentation, in FlxGamepadPointerValueList or in FlxGamepad itself, right next to the pointer object declaration?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would make sense to have some general info on the field in FlxGamepad, and more concrete info on the x/y fields (value range, etc).


#if FLX_JOYSTICK_API
public var hat(default, null):FlxPoint = FlxPoint.get();
Expand All @@ -86,40 +104,93 @@ class FlxGamepad implements IFlxDestroyable
private var _isChrome:Bool = false;
#end

public function new(ID:Int, Manager:FlxGamepadManager, ?Model:FlxGamepadModel)
public function new(ID:Int, Manager:FlxGamepadManager, ?Model:FlxGamepadModel, ?Attachment:FlxGamepadModelAttachment)
{
id = ID;

manager = Manager;

pressed = new FlxGamepadButtonList(FlxInputState.PRESSED, this);
justPressed = new FlxGamepadButtonList(FlxInputState.JUST_PRESSED, this);
justReleased = new FlxGamepadButtonList(FlxInputState.JUST_RELEASED, this);
analog = new FlxGamepadAnalogList(this);
motion = new FlxGamepadMotionValueList(this);
pointer = new FlxGamepadPointerValueList(this);

if (Model == null)
Model = XBox360;

if (Attachment == null)
Attachment = None;

buttonIndex = new FlxGamepadMapping(model);
buttonIndex = new FlxGamepadMapping(model, attachment);
model = Model;
detectedModel = Model;

#if flash
_isChrome = (Capabilities.manufacturer == "Google Pepper");
#end

pressed = new FlxGamepadButtonList(FlxInputState.PRESSED, this);
justPressed = new FlxGamepadButtonList(FlxInputState.JUST_PRESSED, this);
justReleased = new FlxGamepadButtonList(FlxInputState.JUST_RELEASED, this);
analog = new FlxGamepadAnalogList(this);
}

public function traceAxes(f:FlxTimer):Void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These trace functions look like debug code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are, but could be useful to keep around in the API if they're not actually called... maybe? I use them all the time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, not sure I'm a fan of having that be part of the API... Perhaps we could have a FlxGamepad tracker profile instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It takes five seconds to add them in when I need them, I'll just trash em.

{
if (axis == null) return;
if (FlxG.gamepads.lastActive != this) return;
var str:String = "";
for (i in 0...axis.length)
{
var num = Std.int(axis[i] * 1000) / 1000;
str += num;
str += " | ";
}
trace(str);
}

public function traceButtons(f:FlxTimer):Void
{
if (buttons == null) return;
if (FlxG.gamepads.lastActive != this) return;
var str:String = "";
for (i in 0...buttons.length)
{
if (buttons[i] != null)
{
str += buttons[i].ID;
str += ":" + (buttons[i].pressed ? "X":"_");
str += ",";
}
}
trace(str);
}

public function set_model(Model:FlxGamepadModel):FlxGamepadModel
{
model = Model;
buttonIndex.model = Model;

motion.available = buttonIndex.supportsMotion();

#if FLX_JOYSTICK_API
leftStick = getRawAnalogStick(FlxGamepadInputID.LEFT_ANALOG_STICK);
rightStick = getRawAnalogStick(FlxGamepadInputID.RIGHT_ANALOG_STICK);
#end

return model;
}

private var t:FlxTimer = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think this is used anywhere (anymore)?


public function set_attachment(Attachment:FlxGamepadModelAttachment):FlxGamepadModelAttachment
{
attachment = Attachment;
buttonIndex.attachment = Attachment;
#if FLX_JOYSTICK_API
leftStick = getRawAnalogStick(FlxGamepadInputID.LEFT_ANALOG_STICK);
rightStick = getRawAnalogStick(FlxGamepadInputID.RIGHT_ANALOG_STICK);
#end
return attachment;
}

/**
* Returns the "universal" gamepad input ID Given a raw integer.
*/
Expand All @@ -143,6 +214,7 @@ class FlxGamepad implements IFlxDestroyable

public function getButton(RawID:Int):FlxGamepadButton
{
if (RawID == -1) return null;
var gamepadButton:FlxGamepadButton = buttons[RawID];

if (gamepadButton == null)
Expand All @@ -154,6 +226,11 @@ class FlxGamepad implements IFlxDestroyable
return gamepadButton;
}

public inline function getFlipAxis(AxisID:Int):Int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be used to invert the y axis of the Xbox 360 controller in chrome, which is currently hardcoded in getYAxisRaw?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally added it to fix a bug that has since been fixed by other means, but in short, YES. The idea was to move as much weird hardware-and-platform specific fixup code and mapping logic to the SomeControllerID.hx classes and out of the FlxGamepad class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on this, as it was also broken on "Adobe Windows" Capabilities.manufacturer value (ie, the standalone flash player on windows).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix is in now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay maybe I'm crazy, but on my computer chrome doesn't flip the Y axis in flash (so manually inverting it messes it up)... only on the standalone windows player do I have to flip it.

It even says manufacturer = "Google Pepper" in the log trace in flash/chrome, so I dunno...

I'll commit what works here and you tell me what's happening for you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, I didn't remember that correctly. That's what the old code did as well actually.

if (model == XBox360 && !_isChrome)
    axisValue = -axisValue;

{
return buttonIndex.getFlipAxis(AxisID);
}

/**
* Updates the key states (for tracking just pressed, just released, etc).
*/
Expand Down Expand Up @@ -184,6 +261,7 @@ class FlxGamepad implements IFlxDestroyable
button.press();
}
}

#elseif FLX_JOYSTICK_API
for (i in 0...axis.length)
{
Expand All @@ -193,7 +271,7 @@ class FlxGamepad implements IFlxDestroyable
if (button != null)
{
//TODO: account for circular deadzone if an analog stick input is detected?
var value = Math.abs(axis[i]);
var value = Math.abs(axis[i]) * getFlipAxis(i);
if (value > deadZone)
{
button.press();
Expand All @@ -215,8 +293,19 @@ class FlxGamepad implements IFlxDestroyable
button.update();
}
}

if (timecounter > timecount)
{
timecounter = 0;
traceButtons(null);
//traceAxes(null);
}
timecounter += FlxG.elapsed;
}

private var timecounter:Float = 0;
private var timecount:Float = 1 / 16;

public function reset():Void
{
for (button in buttons)
Expand Down Expand Up @@ -489,9 +578,28 @@ class FlxGamepad implements IFlxDestroyable
* use this only for things like FlxGamepadButtonID.LEFT_TRIGGER,
* use getXAxis() / getYAxis() for analog sticks!
*/
public inline function getAxis(AxisButtonID:FlxGamepadInputID):Float
public function getAxis(AxisButtonID:FlxGamepadInputID):Float
{
return getAxisRaw(getRawID(AxisButtonID));
#if !FLX_JOYSTICK_API
return getAxisRaw(getRawID(AxisButtonID));
#else
var fakeAxisRawID:Int = checkForFakeAxis(AxisButtonID);

if (fakeAxisRawID == -1)
{
//return the regular axis value
var rawID = getRawID(AxisButtonID);
return getAxisRaw(rawID) * getFlipAxis(AxisButtonID);
}
else
{
//if analog isn't supported for this input, return the correct digital button input instead
var btn = getButton(fakeAxisRawID);
if (btn == null) return 0;
if (btn.pressed) return 1;
}
return 0;
#end
}

/**
Expand All @@ -518,12 +626,34 @@ class FlxGamepad implements IFlxDestroyable
return buttonIndex.axisIndexToRawID(AxisIndex);
}

public inline function isAxisForAnalogStick(AxisIndex:Int):Bool
public inline function checkForFakeAxis(ID:FlxGamepadInputID):Int
{
return buttonIndex.checkForFakeAxis(ID);
}

public function isAxisForMotion(AxisIndex:Int):Bool
{
return buttonIndex.isAxisForMotion(AxisIndex);
}

public function isAxisForAnalogStick(AxisIndex:Int):Bool
{
if (leftStick != null)
{
if (AxisIndex == leftStick.x || AxisIndex == leftStick.y) return true;
}
if (rightStick != null)
{
if (AxisIndex == rightStick.x || AxisIndex == rightStick.y) return true;
}
return false;
}

public inline function getAnalogStickByAxis(AxisIndex:Int):FlxGamepadAnalogStick
{
return AxisIndex == leftStick.x ||
AxisIndex == leftStick.y ||
AxisIndex == rightStick.x ||
AxisIndex == rightStick.y;
if (leftStick != null && AxisIndex == leftStick.x || AxisIndex == leftStick.y) return leftStick;
if (rightStick != null && AxisIndex == rightStick.x || AxisIndex == rightStick.y) return rightStick;
return null;
}
#end

Expand Down Expand Up @@ -625,6 +755,10 @@ class FlxGamepad implements IFlxDestroyable
var axisValue:Float = 0;

#if FLX_GAMEINPUT_API
if (AxisID == -1)
{
return 0;
}
if ((_device != null) && _device.enabled)
{
axisValue = _device.getControlAt(AxisID).value;
Expand All @@ -643,6 +777,7 @@ class FlxGamepad implements IFlxDestroyable

private function getAnalogXAxisValue(stick:FlxGamepadAnalogStick):Float
{
if (stick == null) return 0;
return if (deadZoneMode == CIRCULAR)
getAnalogAxisValueCircular(stick, stick.x);
else
Expand All @@ -651,6 +786,7 @@ class FlxGamepad implements IFlxDestroyable

private function getAnalogYAxisValue(stick:FlxGamepadAnalogStick):Float
{
if (stick == null) return 0;
return if (deadZoneMode == CIRCULAR)
getAnalogAxisValueCircular(stick, stick.y);
else
Expand All @@ -659,6 +795,7 @@ class FlxGamepad implements IFlxDestroyable

private function getAnalogAxisValueCircular(stick:FlxGamepadAnalogStick, axisID:Int):Float
{
if (stick == null) return 0;
var xAxis = getAxisValue(stick.x);
var yAxis = getAxisValue(stick.y);

Expand Down Expand Up @@ -719,13 +856,55 @@ class FlxGamepadAnalogStick
public var x(default, null):Int;
public var y(default, null):Int;

public function new(x:Int, y:Int)
//these values let the analog stick to send digital inputs to, say, the dpad
public var rawUp(default, null):Int = -1;
public var rawDown(default, null):Int = -1;
public var rawLeft(default, null):Int = -1;
public var rawRight(default, null):Int = -1;

//the value the dpad must be above before digital inputs are sent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want this to appear in completion, it needs to be a /** comment I think.

public var digitalThreshold(default, null):Float = 0.5;

//when analog inputs are received, how to process them digitally
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

public var mode(default, null):AnalogToDigitalMode = SendOnlyAnalog;

public function new(x:Int, y:Int, ?settings:FlxGamepadAnalogStickSettings)
{
this.x = x;
this.y = y;
if (settings != null)
{
mode = (settings.mode != null ? settings.mode : SendOnlyAnalog);
rawUp = (settings.up != null ? settings.up : -1);
rawDown = (settings.down != null ? settings.down : -1);
rawLeft = (settings.left != null ? settings.left : -1);
rawRight = (settings.right != null ? settings.right : -1);
digitalThreshold = (settings.threshold != null ? settings.threshold : -1);
}
}

public function toString():String
{
return("stick(" + x + "," + y + ",(" + rawUp + "," + rawDown + "," + rawLeft + "," + rawRight + ") @" + digitalThreshold + ":" + mode+")");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to use Flixel's standard toString() logic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't know we had such a thing, thx.

}
}

typedef FlxGamepadAnalogStickSettings = {
@:optional var up:Int;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same optional vs ? thing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the ? go before the "var" or before the variable name?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no var with that syntax.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so just like this?

typedef FlxGamepadAnalogStickSettings = {
    ?up:Int;
    ?down:Int;
    ?left:Int;
    ?right:Int;
    ?threshold:Float;
    ?mode:AnalogToDigitalMode;
}

@:optional var down:Int;
@:optional var left:Int;
@:optional var right:Int;
@:optional var threshold:Float;
@:optional var mode:AnalogToDigitalMode;
}

enum AnalogToDigitalMode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Flx prefix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed among other things.

{
SendBoth;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the "Send" add anything here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess not.

SendOnlyDigital;
SendOnlyAnalog;
}

enum FlxGamepadModel
{
Logitech;
Expand All @@ -734,4 +913,13 @@ enum FlxGamepadModel
PS4;
XBox360;
XInput;
MayflashWiiRemote;
WiiRemote;
}

enum FlxGamepadModelAttachment
{
WiiNunchuk;
WiiClassicController;
None;
}
Loading