Wiiリモコンの赤外線センサで位置を取得する際の手振れ補正について.
ざっと思いついた素朴な方法をいくつか
その1:手振れ補正なしの場合
private function onUpdate(event:WiimoteEvent):void {
point.x = 800 - wii.ir.x1 * 800;
point.y = wii.ir.y1 * 600;
}
wii は Wiimote インスタンス.point.x, point.y は Flash上での座標.
手振れ補正なしで Wiiリモコンの位置をディスプレイ上にマッピングすると細かい場所のポインティングが難しい.
その2:ダンパみたいにする
private function onUpdate(event:WiimoteEvent):void {
var px:uint = 800 - wii.ir.x1 * 800;
var py:uint = wii.ir.y1 * 600;
var k:Number = 0.3;
point.x += k * (px - point.x);
point.y += k * (py - point.y);
}
目標移動量に対してk(0~1)倍した分だけポインタを動かす.kが小さいほど追従が遅れるけど反応が鈍いだけ手振れが抑えられる.
その3:非線形マッピング
private function onUpdate(event:WiimoteEvent):void {
var px:uint = 800 - wii.ir.x1 * 800;
var py:uint = wii.ir.y1 * 600;
var dx:int = px - point.x;
var dy:int = py - point.y;
var a:Number = 2;
var b:Number = 200;
point.x += (dx > 0 ? b : -b) * Math.pow(Math.abs(dx) / b, a);
point.y += (dy > 0 ? b : -b) * Math.pow(Math.abs(dy) / b, a);
}
a は 1 より大きい数(指数),b は閾値みたいなもの.
Wiiリモコンの操作量に対するポインタ移動量が,bより小さいときは小さめに,bより大きいときは拡大して反映されるように関数を設定する.
Wiiリモコンが少し動いたぐらいだとポインタはほとんど動かない.最適なパラメータの設定が面倒かも.
その4:2と3の合わせ技
private function onUpdate(event:WiimoteEvent):void {
var px:uint = 800 - wii.ir.x1 * 800;
var py:uint = wii.ir.y1 * 600;
var dx:int = px - point.x;
var dy:int = py - point.y;
var a:Number = 2;
var b:Number = 200;
var k:Number = 0.3;
point.x += (dx > 0 ? b : -b) * k * Math.pow(Math.abs(dx) / b, a);
point.y += (dy > 0 ? b : -b) * k * Math.pow(Math.abs(dy) / b, a);
}
ちゃんとパラメータを設定すればいいのだろうけど,使ってみて違いはあまり分かんない.
Wiiのゲームではどんな方法で手振れ対策がされてるんだろうか
package
{
import flash.display.Sprite;
import flash.events.Event;
import org.wiiflash.events.WiimoteEvent;
import org.wiiflash.Wiimote;
public class Main extends Sprite
{
private var wii:Wiimote;
private var point:Sprite;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
wii = new Wiimote();
wii.connect();
wii.addEventListener(WiimoteEvent.UPDATE, onUpdate);
point = new Sprite();
point.graphics.beginFill(0xffff00);
point.graphics.drawCircle(0, 0, 4);
point.graphics.endFill();
addChild(point);
}
private function onUpdate(event:WiimoteEvent):void {
//ここ
}
}
}