Gainerでの加速度センサによる、オブジェクトの回転、移動

Gainerでの加速度センサによる、オブジェクトの回転、移動。

▼Wonderfl

回転は360度回転したかったし、
移動は回転とは独立な値にしたかったけど、
加速度センサの限界っぽい。

◆360度回転
まず、360度回転は、x軸のみ、y軸のみだったら、問題ない。
ただ、x,y両方を360度回転ではうまくいかない。
いろいろ値をこねくり回してみたが駄目だった。
結局の所、z軸回転をとれないのだから、
正確な姿勢取得ができないという前提に戻ってあきらめた。

◆水平移動の取得
回転とは独立の移動については、
z軸の変化が少ない時に水平移動としてとらえる方法を試したが、
回転値の取得と兼用だとその判別が難しい。
またセンサの誤差も吸収が困難だった。

wiiやPS3で、赤外線センサやジャイロセンサを併用している
理由もなんとなく実感できた。

◆トラッキング
加速度センサはモジュールによって誤差が大きそうだったので、
とりあえず水平時のトラッキング(xとyの中央値、zの最大値)を
取得してから始めるようにした。
もっと正確にやるには、全部の値の最少、中央、最大値を
取得したほうがいいはずだけど、煩雑すぎるような。

▼ActionScript AS3(FP10)
[sourcecode language=”as3″]
/*
* Gainerでの加速度センサによる、オブジェクトの回転、移動。
*
* 回転は360度回転したかったし、
* 移動は回転とは独立な値にしたかったけど、
* 加速度センサの限界っぽい。
*
* ◆360度回転
* まず、360度回転は、x軸のみ、y軸のみだったら、問題ない。
* ただ、x,y両方を360度回転ではうまくいかない。
* いろいろ値をこねくり回してみたが駄目だった。
* 結局の所、z軸回転をとれないのだから、
* 正確な姿勢取得ができないという前提に戻ってあきらめた。
*
* ◆水平移動の取得
* 回転とは独立の移動については、
* z軸の変化が少ない時に水平移動としてとらえる方法を試したが、
* 回転値の取得と兼用だとその判別が難しい。
* またセンサの誤差も吸収が困難だった。
*
* wiiやPS3で、赤外線センサやジャイロセンサを併用している
* 理由もなんとなく実感できた。
*
* ◆トラッキング
* 加速度センサはモジュールによって誤差が大きそうだったので、
* とりあえず水平時のトラッキング(xとyの中央値、zの最大値)を
* 取得してから始めるようにした。
* もっと正確にやるには、全部の値の最少、中央、最大値を
* 取得したほうがいいはずだけど、煩雑すぎるような。
*
* */

package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.display.DisplayObjectContainer;

public class Main extends Sprite {
private var fin:Fin;
public function Main() {

stage.transform.perspectiveProjection = new PerspectiveProjection();
stage.transform.perspectiveProjection.projectionCenter = new Point(465/2,465/2);

fin = new Fin();
fin.z = 0;
fin.x = 465 / 2;
fin.y = 465 / 2+50;
this.addChild(fin);
zSort(fin);

var accelerometerWrapper:AccelerometerWrapper = new AccelerometerWrapper();
this.addChild(accelerometerWrapper);
accelerometerWrapper.onParam = onParam;

}

public function onParam(rArray:Array, tArray:Array):void {
fin.rotationX = rArray[0];
fin.rotationY = rArray[1];
fin.rotationZ = rArray[2];
fin.x += tArray[0];
fin.y += tArray[1];
fin.z += tArray[2];
zSort(fin);
}

private function zSort(target:DisplayObjectContainer):void {
if(!target.root){return};
var n:int = target.numChildren;
var array:Array = [];
var reference:Array = [];
for (var i:int = 0; i < n; i++) {
var poz:Vector3D = target.getChildAt(i).transform.getRelativeMatrix3D(this.root.stage).position;
var point:Point = stage.transform.perspectiveProjection.projectionCenter;
array[i] = poz.subtract(new Vector3D(point.x,point.y,-stage.transform.perspectiveProjection.focalLength)).length;
reference[i] = target.getChildAt(i);
}
var temp:Array = array.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
for (i = 0; i < n; i++) {
target.setChildIndex(reference[temp[i]],0);
if(reference[temp[i]].numChildren > 1){
zSort(reference[temp[i]]);
}
}
}
}
}

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import funnel.*;
import funnel.gui.*;
import funnel.ui.*;

class AccelerometerWrapper extends Sprite {
// Gainerオブジェクト
private var _gainer:Gainer;
private var _textSp:Sprite;
public function AccelerometerWrapper() {
// Gainerのインスタンスを生成
_gainer = new Gainer();

// Gainer GUIのインスタンスを生成して配置
var gui:GainerGUI = new GainerGUI();
//gui.x = 200;
//gui.y = 200;
addChild(gui);
_gainer.gui = gui;

var _textField:TextField = new TextField();
_textField.defaultTextFormat = new TextFormat("_sans", 14, 0xFFFFFF);
_textField.text = "Gainerモジュールを\n平らな机の上に置いて\nモジュール上の\nButtonを押してください。";
_textField.autoSize = "left";

_textSp = new Sprite();
_textSp.graphics.beginFill(0x000000, 0.8);
_textSp.graphics.drawRoundRect(0, 0, _textField.width+32, _textField.height+32, 16, 16);
_textSp.graphics.endFill();
_textSp.x = (465 – _textSp.width) / 2;
_textSp.y = (465 – _textSp.height) / 2+32;
this.addChild(_textSp);

_textField.x = (_textSp.width – _textField.width) / 2;
_textField.y = (_textSp.height – _textField.height) / 2;
_textSp.addChild(_textField);

// ボタンに対してイベントリスナをセット
_gainer.button.addEventListener(ButtonEvent.PRESS, onPress);
_gainer.analogInput(0).filters = [new Convolution(Convolution.MOVING_AVERAGE)];
_gainer.analogInput(1).filters = [new Convolution(Convolution.MOVING_AVERAGE)];
_gainer.analogInput(2).filters = [new Convolution(Convolution.MOVING_AVERAGE)];

}
private function onPress(event:ButtonEvent):void {
trace(event.type);
if (event.type == "press") {
_gainer.button.removeEventListener(ButtonEvent.PRESS, onPress);
this.removeChild(_textSp);
trace(_gainer.analogInput(0).value);
trace(_gainer.analogInput(1).value);
trace(_gainer.analogInput(2).value);

setTrackingValue(_gainer.analogInput(0).value, _gainer.analogInput(1).value, _gainer.analogInput(2).value);

this.removeEventListener(Event.ENTER_FRAME, onEnter);
this.addEventListener(Event.ENTER_FRAME, onEnter);
}
}

private var tracking:Array;
private function setTrackingValue(a0:Number, a1:Number, a2:Number):void {
tracking = [];
tracking[0] = [0.345, 0.584, 0.831];
tracking[1] = [0.328, 0.561, 0.815];
tracking[2] = [0.42, 0.635, 0.853];
tracking[0][1] = a0;
tracking[1][1] = a1;
tracking[2][2] = a2;
}
private function scale0(num:Number):Number {
var y:Number;
if (num < tracking[0][1]) {
y = -(tracking[0][1] – num) / (tracking[0][1] – tracking[0][0]);
}else {
y = (num – tracking[0][1]) / (tracking[0][2] – tracking[0][1]);
}
y = Math.min(1, Math.max(-1, y));
return y;
}
private function scale1(num:Number):Number {
var y:Number;
if (num < tracking[1][1]) {
y = -(tracking[1][1] – num) / (tracking[1][1] – tracking[1][0]);
}else {
y = (num – tracking[1][1]) / (tracking[1][2] – tracking[1][1]);
}
y = Math.min(1, Math.max(-1, y));
return y;
}
private function scale2(num:Number):Number {
var y:Number;
if (num < tracking[2][1]) {
y = -(tracking[2][1] – num) / (tracking[2][1] – tracking[2][0]);
}else {
y = (num – tracking[2][1]) / (tracking[2][2] – tracking[2][1]);
}
y = Math.min(1, Math.max(-1, y));
return y;
}

private function onEnter(event:Event):void {
atParam(_gainer.analogInput(0).value, _gainer.analogInput(1).value, _gainer.analogInput(2).value);
}

public var onParam:Function = function(rArray:Array, tArray:Array):void { };
public function atParam(a0:Number, a1:Number, a2:Number):void {
a0 = scale0(a0);
a1 = scale1(a1);
a2 = scale2(a2);

var rArray:Array = [0, 0, 0];
var tArray:Array = [0, 0, 0];
if(Math.abs(a2)<97){
rArray[0] = Math.asin(a0) / Math.PI * 180;
tArray[0] = a1*5;
rArray[2] = Math.asin(a1) / Math.PI * 180;
tArray[2] = -a0*5;
}
onParam(rArray, tArray);
}
}

import flash.display.Sprite;
class Fin extends Sprite {
public function Fin() {
var xyr:Array = [[90, 0, 0, 0xFF0000], [0, 0, 0, 0x00FF00], [0, 90, 0, 0x0000FF]];
for (var j:int = 0; j < 3; j++) {
for (var i:int = 0; i < 4; i++) {
var sp:Sprite = new Sprite();
sp.graphics.lineStyle(1, xyr[j][3], 0.5);
sp.graphics.beginFill(xyr[j][3], 0.25);
sp.graphics.drawRect(-25, -25, 50, 50);
sp.graphics.endFill();
sp.rotationX = xyr[j][0];
sp.rotationY = xyr[j][1];
if (j == 0) {
sp.x = 25-50 * (i % 2);
sp.z = 25-50*(Math.floor(i/2));
}else if (j == 1) {
sp.x = 25-50 * (i % 2);
sp.y = 25-50*(Math.floor(i/2));
}else if (j == 2) {
sp.y = 25-50 * (i % 2);
sp.z = 25-50*(Math.floor(i/2));
}

this.addChild(sp);
}
}

}
}
[/sourcecode]