1 /** 2 * The Render Engine 3 * WiimoteInputComponent 4 * 5 * @fileoverview An extension of the keyboard input component which handles the 6 * Nintendo Wii remote. 7 * 8 * @author: Brett Fattori (brettf@renderengine.com) 9 * @author: $Author: bfattori $ 10 * @version: $Revision: 1555 $ 11 * 12 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 13 * 14 * Permission is hereby granted, free of charge, to any person obtaining a copy 15 * of this software and associated documentation files (the "Software"), to deal 16 * in the Software without restriction, including without limitation the rights 17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 * copies of the Software, and to permit persons to whom the Software is 19 * furnished to do so, subject to the following conditions: 20 * 21 * The above copyright notice and this permission notice shall be included in 22 * all copies or substantial portions of the Software. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 * THE SOFTWARE. 31 * 32 */ 33 34 // The class this file defines and its required classes 35 R.Engine.define({ 36 "class":"R.components.input.Wiimote", 37 "requires":[ 38 "R.components.input.Keyboard", 39 "R.math.Math2D", 40 "R.math.Point2D", 41 "R.math.Vector2D", 42 "R.math.Rectangle2D" 43 ] 44 }); 45 46 /** 47 * @class A component which responds to the Wiimote (in the Opera browser) 48 * is notifies its host object by calling a number of methods. The host object 49 * should implement any of the following methods to receive the corresponding events. 50 * <br/> 51 * The next few events are fired on the host object, if they exist, when 52 * the corresponding button is pressed and released. All methods take three 53 * arguments: the controller number, a boolean indicating <tt>true</tt> 54 * if the button has been pressed or <tt>false</tt> when released, and 55 * the event object that caused the method to be invoked. 56 * <br/> 57 * <ul> 58 * <li><tt>onWiimoteLeft()</tt> - Direction pad left</li> 59 * <li><tt>onWiimoteRight()</tt> - Direction pad right</li> 60 * <li><tt>onWiimoteUp()</tt> - Direction pad up</li> 61 * <li><tt>onWiimoteDown()</tt> - Direction pad down</li> 62 * <li><tt>onWiimotePlus()</tt> - Plus button pressed/released</li> 63 * <li><tt>onWiimoteMinus()</tt> - Minus button pressed/released</li> 64 * <li><tt>onWiimoteButton1()</tt> - Button 1 pressed/released</li> 65 * <li><tt>onWiimoteButton2()</tt> - Button 2 pressed/released</li> 66 * <li><tt>onWiimoteButtonA()</tt> - Button A pressed/released</li> 67 * <li><tt>onWiimoteButtonB()</tt> - Button B pressed/released</li> 68 * <li><tt>onWiimoteButtonC()</tt> - Button C pressed/released</li> 69 * <li><tt>onWiimoteButtonZ()</tt> - Button Z pressed/released</li> 70 * </ul> 71 * <br/><br/> 72 * The following events are status events and take different 73 * arguments: 74 * <ul> 75 * <li><tt>onWiimoteEnabled()</tt> - Enabled/disabled status (controller, state)</li> 76 * <li><tt>onWiimoteValidity()</tt> - Validity of data transfer (controller, validity)</li> 77 * <li><tt>onWiimoteDistance()</tt> - Distance from screen in meters (controller, dist)</li> 78 * <li><tt>onWiimotePosition()</tt> - X/Y position (controller, x, y)</li> 79 * <li><tt>onWiimoteRoll()</tt> - X-axis roll in radians (controller, roll)</li> 80 * <li><tt>onWiimoteOffscreen()</tt> - Triggered <i>instead of</i> onWiimotePosition if the 81 * remote isn't pointing at the screen.</li> 82 * </ul> 83 * 84 * @param name {String} The name of the component 85 * @param [priority=0.1] {Number} The priority of the component 86 * @extends R.components.input.Keyboard 87 * @constructor 88 * @description Create a Wii remote input component. 89 */ 90 R.components.input.Wiimote = function () { 91 "use strict"; 92 return R.components.input.Keyboard.extend(/** @scope R.components.input.Wiimote.prototype */{ 93 94 enabledRemotes:null, 95 96 remoteValid:null, 97 98 hasMethods:null, 99 100 /** 101 * @private 102 */ 103 constructor:function (name, priority) { 104 this.base(name, priority || 0.1); 105 this.enabledRemotes = [false, false, false, false]; 106 this.remoteValid = [0, 0, 0, 0]; 107 108 var ctx = R.Engine.getDefaultContext(); 109 var self = this; 110 111 // Add the event handlers 112 ctx.addEvent(this, "mousedown", function (evt) { 113 self._mouseDownListener(evt); 114 }); 115 116 ctx.addEvent(this, "mouseup", function (evt) { 117 self._mouseUpListener(evt); 118 }); 119 120 if (!R.browser.Wii) { 121 // In the absense of the WiiMote, we'll use the mouse as the pointer 122 ctx.addEvent(this, "mousemove", function (evt) { 123 self._mouseMoveListener(evt); 124 }); 125 } 126 127 this.hasMethods = [false, false, false, false, false, false, 128 false, false, false, false, false, false, 129 false, false, false, false, false, false]; 130 }, 131 132 /** 133 * Releases the component back into the object pool. See {@link R.engine.PooledObject#release} 134 * for more information. 135 */ 136 release:function () { 137 this.base(); 138 this.enabledRemotes = null; 139 this.remoteValid = null; 140 this.hasMethods = null; 141 }, 142 143 /** 144 * Destroy this instance and remove all references. 145 */ 146 destroy:function () { 147 var ctx = R.Engine.getDefaultContext(); 148 149 // Clean up event handlers 150 ctx.removeEvent(this, "mousedown"); 151 ctx.removeEvent(this, "mouseup"); 152 153 if (!R.browser.Wii) { 154 // In the absence of the WiiMote, remove the mouse move handler 155 ctx.removeEvent(this, "mousemove"); 156 } 157 this.base(); 158 }, 159 160 /** 161 * Deprecated in favor of {@link #setGameObject} 162 * @deprecated 163 */ 164 setHostObject:function (hostObj) { 165 this.setGameObject(hostObj); 166 }, 167 168 /** 169 * Establishes the link between this component and its game object. 170 * When you assign components to a game object, it will call this method 171 * so that each component can refer to its game object, the same way 172 * a game object can refer to a component with {@link R.engine.GameObject#getComponent}. 173 * 174 * @param gameObject {R.engine.GameObject} The object which hosts this component 175 */ 176 setGameObject:function (gameObject) { 177 this.base(gameObject); 178 this.hasMethods = [gameObject.onWiimoteLeft != undefined, 179 gameObject.onWiimoteRight != undefined, 180 gameObject.onWiimoteUp != undefined, 181 gameObject.onWiimoteDown != undefined, 182 gameObject.onWiimotePlus != undefined, 183 gameObject.onWiimoteMinus != undefined, 184 gameObject.onWiimoteButton1 != undefined, 185 gameObject.onWiimoteButton2 != undefined, 186 gameObject.onWiimoteButtonA != undefined, 187 gameObject.onWiimoteButtonB != undefined, 188 gameObject.onWiimoteButtonC != undefined, 189 gameObject.onWiimoteButtonZ != undefined, 190 gameObject.onWiimoteEnabled != undefined, 191 gameObject.onWiimoteDistance != undefined, 192 gameObject.onWiimoteValidity != undefined, 193 gameObject.onWiimoteOffscreen != undefined, 194 gameObject.onWiimotePosition != undefined, 195 gameObject.onWiimoteRoll != undefined]; 196 }, 197 198 199 /** @private */ 200 _mouseDownListener:function (evt) { 201 this._wmButtonA(evt, 0, true); 202 }, 203 204 /** @private */ 205 _mouseUpListener:function (evt) { 206 this._wmButtonA(evt, 0, false); 207 }, 208 209 /** @private */ 210 _mouseMoveListener:function (evt) { 211 this._wmPosition(0, evt.pageX, evt.pageY, evt.screenX, evt.screenY); 212 }, 213 214 /** @private */ 215 _keyDownListener:function (event) { 216 // This is for handling the Primary Wiimote 217 switch (event.keyCode) { 218 case R.components.input.Wiimote.KEYCODE_LEFT: 219 this._wmLeft(event, 0, true); 220 break; 221 case R.components.input.Wiimote.KEYCODE_RIGHT: 222 this._wmRight(event, 0, true); 223 break; 224 case R.components.input.Wiimote.KEYCODE_UP: 225 this._wmUp(event, 0, true); 226 break; 227 case R.components.input.Wiimote.KEYCODE_DOWN: 228 this._wmDown(event, 0, true); 229 break; 230 case R.components.input.Wiimote.KEYCODE_PLUS: 231 this._wmPlus(event, 0, true); 232 break; 233 case R.components.input.Wiimote.KEYCODE_MINUS: 234 this._wmMinus(event, 0, true); 235 break; 236 case R.components.input.Wiimote.KEYCODE_1: 237 this._wmButton1(event, 0, true); 238 break; 239 case R.components.input.Wiimote.KEYCODE_2: 240 this._wmButton2(event, 0, true); 241 break; 242 case R.components.input.Wiimote.KEYCODE_A: 243 this._wmButtonA(event, 0, true); 244 break; 245 case R.components.input.Wiimote.KEYCODE_B: 246 this._wmButtonB(event, 0, true); 247 break; 248 case R.components.input.Wiimote.KEYCODE_C: 249 return this._wmButtonC(event, 0, true); 250 break; 251 case R.components.input.Wiimote.KEYCODE_Z: 252 this._wmButtonZ(event, 0, true); 253 break; 254 } 255 256 // Pass along for straight keyboard handling 257 this.base(event); 258 }, 259 260 /** @private */ 261 _keyUpListener:function (event) { 262 // This is for handling the Primary Wiimote 263 switch (event.keyCode) { 264 case R.components.input.Wiimote.KEYCODE_LEFT: 265 this._wmLeft(event, 0, false); 266 break; 267 case R.components.input.Wiimote.KEYCODE_RIGHT: 268 this._wmRight(event, 0, false); 269 break; 270 case R.components.input.Wiimote.KEYCODE_UP: 271 this._wmUp(event, 0, false); 272 break; 273 case R.components.input.Wiimote.KEYCODE_DOWN: 274 this._wmDown(event, 0, false); 275 break; 276 case R.components.input.Wiimote.KEYCODE_PLUS: 277 this._wmPlus(event, 0, false); 278 break; 279 case R.components.input.Wiimote.KEYCODE_MINUS: 280 this._wmMinus(event, 0, false); 281 break; 282 case R.components.input.Wiimote.KEYCODE_1: 283 this._wmButton1(event, 0, false); 284 break; 285 case R.components.input.Wiimote.KEYCODE_2: 286 this._wmButton2(event, 0, false); 287 break; 288 case R.components.input.Wiimote.KEYCODE_A: 289 this._wmButtonA(event, 0, false); 290 break; 291 case R.components.input.Wiimote.KEYCODE_B: 292 this._wmButtonB(event, 0, false); 293 break; 294 case R.components.input.Wiimote.KEYCODE_C: 295 this._wmButtonC(event, 0, false); 296 break; 297 case R.components.input.Wiimote.KEYCODE_Z: 298 this._wmButtonZ(event, 0, false); 299 break; 300 } 301 302 // Pass along for straight keyboard handling 303 this.base(event); 304 }, 305 306 /** 307 * This will do the polling of the Wiimote and fire events when 308 * statuses change. 309 * 310 * @private 311 */ 312 execute:function (renderContext, time, dt) { 313 if (!R.browser.Wii) { 314 // If this isn't Opera for Wii, don't do anything 315 return; 316 } 317 318 // Run through the available Wiimotes 319 var op = R.browser.WiiMote; 320 for (var w = 0; w < 4; w++) { 321 322 var remote = op.update(w); // This fixes a dependency problem 323 // Cannot perform this check on the primary remote, 324 // that's why this object extends the keyboard input component... 325 if (remote.isEnabled) { 326 327 if (!this.enabledRemotes[w]) { 328 // Let the host know that a Wiimote became enabled 329 this._wmEnabled(w, true); 330 } 331 332 if (!remote.isBrowsing) { 333 var evt = { primary:false }; 334 335 // Simple bitmask check to handle states and fire methods 336 this._wmLeft(evt, w, remote.hold & 1); 337 this._wmRight(evt, w, remote.hold & 2); 338 this._wmDown(evt, w, remote.hold & 4); 339 this._wmUp(evt, w, remote.hold & 8); 340 this._wmPlus(evt, w, remote.hold & 16); 341 this._wmButton2(evt, w, remote.hold & 256); 342 this._wmButton1(evt, w, remote.hold & 512); 343 this._wmButtonA(evt, w, remote.hold & 2048); 344 this._wmMinus(evt, w, remote.hold & 4096); 345 this._wmButtonZ(evt, w, remote.hold & 8192); 346 this._wmButtonC(evt, w, remote.hold & 16384); 347 } 348 349 this._wmButtonB(evt, w, remote.hold & 1024); 350 351 // Set validity of remote data 352 this._wmValidity(w, remote.dpdValidity); 353 354 // Set distance to screen 355 this._wmDistance(w, remote.dpdDistance); 356 357 // Set position and roll 358 this._wmPosition(w, remote.dpdScreenX, remote.dpdScreenY, remote.dpdX, remote.dpdY); 359 this._wmRoll(w, remote.dpdRollX, remote.dpdRollY, Math.atan2(remote.dpdRollY, remote.dpdRollX)); 360 } else { 361 if (this.enabledRemotes[w]) { 362 // Let the host know that a Wiimote became disabled 363 this._wmEnabled(w, false); 364 } 365 } 366 } 367 }, 368 369 /** @private */ 370 _wmLeft:function (evt, controllerNum, pressed) { 371 if (this.hasMethods[0]) { 372 this.getGameObject().onWiimoteLeft(controllerNum, pressed, evt); 373 } 374 }, 375 376 /** @private */ 377 _wmRight:function (evt, controllerNum, pressed) { 378 if (this.hasMethods[1]) { 379 this.getGameObject().onWiimoteRight(controllerNum, pressed, evt); 380 } 381 }, 382 383 /** @private */ 384 _wmUp:function (evt, controllerNum, pressed) { 385 if (this.hasMethods[2]) { 386 this.getGameObject().onWiimoteUp(controllerNum, pressed, evt); 387 } 388 }, 389 390 /** @private */ 391 _wmDown:function (evt, controllerNum, pressed) { 392 if (this.hasMethods[3]) { 393 this.getGameObject().onWiimoteDown(controllerNum, pressed, evt); 394 } 395 }, 396 397 /** @private */ 398 _wmPlus:function (evt, controllerNum, pressed) { 399 if (this.hasMethods[4]) { 400 this.getGameObject().onWiimotePlus(controllerNum, pressed, evt); 401 } 402 }, 403 404 /** @private */ 405 _wmMinus:function (evt, controllerNum, pressed) { 406 if (this.hasMethods[5]) { 407 this.getGameObject().onWiimoteMinus(controllerNum, pressed, evt); 408 } 409 }, 410 411 /** @private */ 412 _wmButton1:function (evt, controllerNum, pressed) { 413 if (this.hasMethods[6]) { 414 this.getGameObject().onWiimoteButton1(controllerNum, pressed, evt); 415 } 416 }, 417 418 /** @private */ 419 _wmButton2:function (evt, controllerNum, pressed) { 420 if (this.hasMethods[7]) { 421 this.getGameObject().onWiimoteButton2(controllerNum, pressed, evt); 422 } 423 }, 424 425 /** @private */ 426 _wmButtonA:function (evt, controllerNum, pressed) { 427 if (this.hasMethods[8]) { 428 this.getGameObject().onWiimoteButtonA(controllerNum, pressed, evt); 429 } 430 }, 431 432 /** @private */ 433 _wmButtonB:function (evt, controllerNum, pressed) { 434 if (this.hasMethods[9]) { 435 this.getGameObject().onWiimoteButtonB(controllerNum, pressed, evt); 436 } 437 }, 438 439 /** @private */ 440 _wmButtonC:function (evt, controllerNum, pressed) { 441 if (this.hasMethods[10]) { 442 this.getGameObject().onWiimoteButtonC(controllerNum, pressed, evt); 443 } 444 }, 445 446 /** @private */ 447 _wmButtonZ:function (evt, controllerNum, pressed) { 448 if (this.hasMethods[11]) { 449 this.getGameObject().onWiimoteButtonZ(controllerNum, pressed, evt); 450 } 451 }, 452 453 /** @private */ 454 _wmEnabled:function (controllerNum, state) { 455 // Store the Wiimote enabled state 456 this.enabledRemotes[controllerNum] = state; 457 if (this.hasMethods[12]) { 458 this.getGameObject().onWiimoteEnabled(controllerNum, state); 459 } 460 }, 461 462 /** @private */ 463 _wmDistance:function (c, d) { 464 if (this.hasMethods[13]) { 465 this.getGameObject().onWiimoteDistance(c, d); 466 } 467 }, 468 469 /** @private */ 470 _wmValidity:function (c, v) { 471 if (this.remoteValid[c] != v) { 472 this.remoteValid[c] = v; 473 if (this.hasMethods[14]) { 474 this.getGameObject().onWiimoteValidity(c, v); 475 } 476 } 477 }, 478 479 /** @private */ 480 _wmPosition:function (c, sx, sy, x, y) { 481 if (this.hasMethods[15]) { 482 this.getGameObject().onWiimoteOffscreen(c, (!sx || !sy)); 483 } 484 485 if ((sx && sy) && this.hasMethods[16]) { 486 this.getGameObject().onWiimotePosition(c, sx, sy, x, y); 487 } 488 }, 489 490 /** @private */ 491 _wmRoll:function (c, x, y, z) { 492 if (this.hasMethods[17]) { 493 // Pitch, yaw, roll? 494 this.getGameObject().onWiimoteRoll(c, x, y, z); 495 } 496 } 497 }, /** @scope R.components.input.Wiimote.prototype */{ 498 499 /** 500 * Get the class name of this object 501 * 502 * @return {String} "R.components.input.Wiimote" 503 */ 504 getClassName:function () { 505 return "R.components.input.Wiimote"; 506 }, 507 508 /** 509 * For second argument to <tt>onWiimoteValidity()</tt>: the data is good 510 * @type {Number} 511 */ 512 DATA_GOOD:2, 513 514 /** 515 * For second argument to <tt>onWiimoteValidity()</tt>: the data is poor 516 * @type {Number} 517 */ 518 DATA_POOR:1, 519 520 /** 521 * For second argument to <tt>onWiimoteValidity()</tt>: the Wiimote isn't pointing at the screen 522 * @type {Number} 523 */ 524 DATA_INVALID:0, 525 526 /** 527 * For second argument to <tt>onWiimoteValidity()</tt>: the data is very poor (unreliable) 528 * @type {Number} 529 */ 530 DATA_VERY_POOR:-1, 531 532 /** 533 * For second argument to <tt>onWiimoteValidity()</tt>: the data is extremely poor (garbage) 534 * @type {Number} 535 */ 536 DATA_EXTREMELY_POOR:-2, 537 538 /** 539 * Keycode for button "A" 540 * @type {Number} 541 */ 542 KEYCODE_A:13, 543 544 /** 545 * Keycode for button "B" 546 * @type {Number} 547 */ 548 KEYCODE_B:32, // 171 549 550 /** 551 * Keycode for button "C" 552 * @type {Number} 553 */ 554 KEYCODE_C:67, // 201 555 556 /** 557 * Keycode for button "Z" 558 * @type {Number} 559 */ 560 KEYCODE_Z:90, // 200 561 562 /** 563 * Keycode for button "1" 564 * @type {Number} 565 */ 566 KEYCODE_1:173, 567 568 /** 569 * Keycode for button "2" 570 * @type {Number} 571 */ 572 KEYCODE_2:173, 573 574 /** 575 * Keycode for button "-" 576 * @type {Number} 577 */ 578 KEYCODE_MINUS:170, 579 580 /** 581 * Keycode for button "+" 582 * @type {Number} 583 */ 584 KEYCODE_PLUS:174, 585 586 /** 587 * Keycode for dpad left 588 * @type {Number} 589 */ 590 KEYCODE_LEFT:178, 591 592 /** 593 * Keycode for dpad right 594 * @type {Number} 595 */ 596 KEYCODE_RIGHT:177, 597 598 /** 599 * Keycode for dpad up 600 * @type {Number} 601 */ 602 KEYCODE_UP:175, 603 604 /** 605 * Keycode for dpad down 606 * @type {Number} 607 */ 608 KEYCODE_DOWN:176 609 }); 610 }; 611